From eea498f976fc1e702ccd31a3ea012a7849f201ad Mon Sep 17 00:00:00 2001
From: Lukas Stadler <lukas.stadler@oracle.com>
Date: Wed, 17 Feb 2016 16:21:18 +0100
Subject: [PATCH] add math and stats functions to provide qbinom

---
 .../oracle/truffle/r/library/stats/DPQ.java   |   76 +
 .../r/library/stats/MathConstants.java        |   91 +
 .../truffle/r/library/stats/MathInit.java     |   84 +
 .../oracle/truffle/r/library/stats/Pbeta.java |   80 +
 .../truffle/r/library/stats/Pbinom.java       |   49 +
 .../truffle/r/library/stats/Qbinom.java       |  198 ++
 .../truffle/r/library/stats/TOMS708.java      | 2354 +++++++++++++++++
 .../base/foreign/ForeignFunctions.java        |    2 +
 mx.fastr/copyrights/gnu_r.core.copyright.star |   10 +
 .../gnu_r.core.copyright.star.regex           |    1 +
 .../gnu_r_ihaka.copyright.star.regex          |    2 +-
 mx.fastr/copyrights/overrides                 |    7 +
 12 files changed, 2953 insertions(+), 1 deletion(-)
 create mode 100644 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/DPQ.java
 create mode 100644 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/MathConstants.java
 create mode 100644 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/MathInit.java
 create mode 100644 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Pbeta.java
 create mode 100644 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Pbinom.java
 create mode 100644 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Qbinom.java
 create mode 100644 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/TOMS708.java
 create mode 100644 mx.fastr/copyrights/gnu_r.core.copyright.star
 create mode 100644 mx.fastr/copyrights/gnu_r.core.copyright.star.regex

diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/DPQ.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/DPQ.java
new file mode 100644
index 0000000000..98847ab568
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/DPQ.java
@@ -0,0 +1,76 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 2000--2014, The R Core Team
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.stats;
+
+// transcribed from dpq.h
+
+public final class DPQ {
+    private DPQ() {
+        // private
+    }
+
+    // R >= 3.1.0: # define R_nonint(x) (fabs((x) - R_forceint(x)) > 1e-7)
+    public static boolean nonint(double x) {
+        return (Math.abs((x) - Math.round(x)) > 1e-7 * Math.max(1., Math.abs(x)));
+    }
+
+    // R_D__0
+    public static double d0(boolean logP) {
+        return (logP ? Double.NEGATIVE_INFINITY : 0.);
+    }
+
+    // R_D__1
+    public static double d1(boolean logP) {
+        return (logP ? 0. : 1.);
+    }
+
+    // R_DT_0
+    public static double dt0(boolean logP, boolean lowerTail) {
+        return (lowerTail ? d0(logP) : d1(logP));
+    }
+
+    // R_DT_1
+    public static double dt1(boolean logP, boolean lowerTail) {
+        return (lowerTail ? d1(logP) : d0(logP));
+    }
+
+    /* Use 0.5 - p + 0.5 to perhaps gain 1 bit of accuracy */
+
+    // R_D_Lval
+    public static double dLval(boolean lowerTail, double p) {
+        return (lowerTail ? (p) : (0.5 - (p) + 0.5));
+    }
+
+    // #define R_D_Cval(p) (lower_tail ? (0.5 - (p) + 0.5) : (p)) /* 1 - p */
+    //
+    // #define R_D_val(x) (log_p ? log(x) : (x)) /* x in pF(x,..) */
+    // #define R_D_qIv(p) (log_p ? exp(p) : (p)) /* p in qF(p,..) */
+    // R_D_exp
+    public static double dExp(double x, boolean logP) {
+        return logP ? (x) : Math.exp(x); /* exp(x) */
+    }
+
+    // #define R_D_log(p) (log_p ? (p) : log(p)) /* log(p) */
+    // #define R_D_Clog(p) (log_p ? log1p(-(p)) : (0.5 - (p) + 0.5)) /* [log](1-p) */
+    //
+    // // log(1 - exp(x)) in more stable form than log1p(- R_D_qIv(x)) :
+    // #define R_Log1_Exp(x) ((x) > -M_LN2 ? log(-expm1(x)) : log1p(-exp(x)))
+    //
+    // #define R_D_LExp(x) (log_p ? R_Log1_Exp(x) : log1p(-x))
+    //
+    // #define R_DT_val(x) (lower_tail ? R_D_val(x) : R_D_Clog(x))
+    // #define R_DT_Cval(x) (lower_tail ? R_D_Clog(x) : R_D_val(x))
+    //
+    // R_DT_qIv
+    public static double dtQIv(boolean logP, boolean lowerTail, double p) {
+        return (logP ? (lowerTail ? Math.exp(p) : -Math.expm1(p)) : dLval(lowerTail, p));
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/MathConstants.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/MathConstants.java
new file mode 100644
index 0000000000..db75147af8
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/MathConstants.java
@@ -0,0 +1,91 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 1995-2012, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.stats;
+
+// transcribed from Rmath.h
+
+public final class MathConstants {
+    private MathConstants() {
+        // private
+    }
+
+/* ----- The following constants and entry points are part of the R API ---- */
+
+/* 30 Decimal-place constants */
+/* Computed with bc -l (scale=32; proper round) */
+
+/* SVID & X/Open Constants */
+/* Names from Solaris math.h */
+
+    // e
+    public static final double M_E = 2.718281828459045235360287471353;
+    // log2(e)
+    public static final double M_LOG2E = 1.442695040888963407359924681002;
+    // log10(e)
+    public static final double M_LOG10E = 0.434294481903251827651128918917;
+    // ln(2)
+    public static final double M_LN2 = 0.693147180559945309417232121458;
+    // ln(10)
+    public static final double M_LN10 = 2.302585092994045684017991454684;
+    // pi
+    public static final double M_PI = 3.141592653589793238462643383280;
+    // 2*pi
+    public static final double M_2PI = 6.283185307179586476925286766559;
+    // pi/2
+    public static final double M_PI_2 = 1.570796326794896619231321691640;
+    // pi/4
+    public static final double M_PI_4 = 0.785398163397448309615660845820;
+    // 1/pi
+    public static final double M_1_PI = 0.318309886183790671537767526745;
+    // 2/pi
+    public static final double M_2_PI = 0.636619772367581343075535053490;
+    // 2/sqrt(pi)
+    public static final double M_2_SQRTPI = 1.128379167095512573896158903122;
+    // sqrt(2)
+    public static final double M_SQRT2 = 1.414213562373095048801688724210;
+    // 1/sqrt(2)
+    public static final double M_SQRT1_2 = 0.707106781186547524400844362105;
+
+/* R-Specific Constants */
+    // sqrt(3)
+    public static final double M_SQRT_3 = 1.732050807568877293527446341506;
+    // sqrt(32)
+    public static final double M_SQRT_32 = 5.656854249492380195206754896838;
+    // log10(2)
+    public static final double M_LOG10_2 = 0.301029995663981195213738894724;
+    // sqrt(pi)
+    public static final double M_SQRT_PI = 1.772453850905516027298167483341;
+    // 1/sqrt(2pi)
+    public static final double M_1_SQRT_2PI = 0.398942280401432677939946059934;
+    // sqrt(2/pi)
+    public static final double M_SQRT_2dPI = 0.797884560802865355879892119869;
+    // log(2*pi)
+    public static final double M_LN_2PI = 1.837877066409345483560659472811;
+    // log(sqrt(pi)) == log(pi)/2
+    public static final double M_LN_SQRT_PI = 0.572364942924700087071713675677;
+    // log(sqrt(2*pi)) == log(2*pi)/2
+    public static final double M_LN_SQRT_2PI = 0.918938533204672741780329736406;
+    // log(sqrt(pi/2)) == log(pi/2)/2
+    public static final double M_LN_SQRT_PId2 = 0.225791352644727432363097614947;
+
+    /**
+     * Compute the log of a sum from logs of terms, i.e.,
+     *
+     * log (exp (logx) + exp (logy))
+     *
+     * without causing overflows and without throwing away large handfuls of accuracy.
+     */
+    // logspace_add
+    public static double logspaceAdd(double logx, double logy) {
+        return Math.max(logx, logy) + Math.log1p(Math.exp(-Math.abs(logx - logy)));
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/MathInit.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/MathInit.java
new file mode 100644
index 0000000000..bcd6ce6007
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/MathInit.java
@@ -0,0 +1,84 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 2001--2012, The R Core Team
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.stats;
+
+import static com.oracle.truffle.r.library.stats.MathConstants.M_LOG10_2;
+
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
+
+// transcribed from pbeta.c
+
+public final class MathInit {
+    private MathInit() {
+        // private
+    }
+
+    // Rf_d1mach transcribed from d1mach.c
+    public static double d1mach(int i) {
+        switch (i) {
+            case 1:
+                return Double.MIN_VALUE;
+            case 2:
+                return Double.MAX_VALUE;
+            case 3:
+                // FLT_RADIX ^ - DBL_MANT_DIG for IEEE: = 2^-53 = 1.110223e-16 = .5*DBL_EPSILON
+                return 0.5 * RRuntime.EPSILON;
+            case 4:
+                // FLT_RADIX ^ (1- DBL_MANT_DIG) = for IEEE: = 2^-52 = DBL_EPSILON
+                return RRuntime.EPSILON;
+            case 5:
+                return M_LOG10_2;
+            default:
+                return 0.0;
+        }
+    }
+
+    // Rf_i1mach transcribed from init.c
+    public static int i1mach(int i) {
+        switch (i) {
+            case 1:
+                return 5;
+            case 2:
+                return 6;
+            case 3:
+                return 0;
+            case 4:
+                return 0;
+
+// case 5: return CHAR_BIT * sizeof(int);
+// case 6: return sizeof(int)/sizeof(char);
+
+            case 7:
+                return 2;
+// case 8: return CHAR_BIT * sizeof(int) - 1;
+            case 9:
+                return Integer.MAX_VALUE;
+
+// case 10: return FLT_RADIX;
+//
+// case 11: return FLT_MANT_DIG;
+// case 12: return FLT_MIN_EXP;
+// case 13: return FLT_MAX_EXP;
+//
+// case 14: return DBL_MANT_DIG;
+            case 15:
+                return Double.MIN_EXPONENT;
+            case 16:
+                return Double.MAX_EXPONENT;
+
+            default:
+                throw RInternalError.shouldNotReachHere();
+// return 0;
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Pbeta.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Pbeta.java
new file mode 100644
index 0000000000..f67cfdda7c
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Pbeta.java
@@ -0,0 +1,80 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 2006, The R Core Team
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.stats;
+
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+
+// transcribed from pbeta.c
+
+public abstract class Pbeta extends RExternalBuiltinNode.Arg5 {
+
+    private static double pbetaRaw(double x, double a, double b, boolean lowerTail, boolean logP) {
+        // treat limit cases correctly here:
+        if (a == 0 || b == 0 || !Double.isFinite(a) || !Double.isFinite(b)) {
+            // NB: 0 < x < 1 :
+            if (a == 0 && b == 0) {
+                // point mass 1/2 at each of {0,1} :
+                return (logP ? -MathConstants.M_LN2 : 0.5);
+            }
+            if (a == 0 || a / b == 0) {
+                // point mass 1 at 0 ==> P(X <= x) = 1, all x > 0
+                return DPQ.dt1(logP, lowerTail);
+            }
+            if (b == 0 || b / a == 0) {
+                // point mass 1 at 1 ==> P(X <= x) = 0, all x < 1
+                return DPQ.dt0(logP, lowerTail);
+            }
+
+            // else, remaining case: a = b = Inf : point mass 1 at 1/2
+            if (x < 0.5) {
+                return DPQ.dt0(logP, lowerTail);
+            }
+            // else, x >= 0.5 :
+            return DPQ.dt1(logP, lowerTail);
+        }
+        // Now: 0 < a < Inf; 0 < b < Inf
+
+        double x1 = 0.5 - x + 0.5;
+        // ====
+        TOMS708.Bratio bratio = new TOMS708.Bratio();
+        bratio.bratio(a, b, x, x1, logP); /* -> ./toms708.c */
+        // ====
+        /* ierr = 8 is about inaccuracy in extreme cases */
+        if (bratio.ierr != 0 && (bratio.ierr != 8 || logP)) {
+            RError.warning(RError.SHOW_CALLER, Message.GENERIC, String.format("pbeta_raw(%g, a=%g, b=%g, ..) -> bratio() gave error code %d",
+                            x, a, b, bratio.ierr));
+        }
+        return lowerTail ? bratio.w : bratio.w1;
+    }/* pbeta_raw() */
+
+    static double pbeta(double x, double a, double b, boolean lowerTail, boolean logP) {
+        if (Double.isNaN(x) || Double.isNaN(a) || Double.isNaN(b)) {
+            return x + a + b;
+        }
+
+        if (a < 0 || b < 0) {
+            return Double.NaN;
+        }
+        // allowing a==0 and b==0 <==> treat as one- or two-point mass
+
+        if (x <= 0) {
+            return DPQ.dt0(logP, lowerTail);
+        }
+        if (x >= 1) {
+            return DPQ.dt1(logP, lowerTail);
+        }
+
+        return pbetaRaw(x, a, b, lowerTail, logP);
+    }
+
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Pbinom.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Pbinom.java
new file mode 100644
index 0000000000..5e4e741c3d
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Pbinom.java
@@ -0,0 +1,49 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (c) 2000--2014, The R Core Team
+ * Copyright (c) 2004, The R Foundation
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.stats;
+
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.RError;
+
+public abstract class Pbinom extends RExternalBuiltinNode.Arg5 {
+
+    static double pbinom(double initialX, double initialN, double p, boolean lowerTail, boolean logP) {
+        double x = initialX;
+        double n = initialN;
+        if (Double.isNaN(x) || Double.isNaN(n) || Double.isNaN(p)) {
+            return x + n + p;
+        }
+        if (!Double.isFinite(n) || !Double.isFinite(p)) {
+            return Double.NaN;
+        }
+
+        if (DPQ.nonint(n)) {
+            RError.warning(RError.SHOW_CALLER, RError.Message.GENERIC, String.format("non-integer n = %f", n));
+            return Double.NaN;
+        }
+        n = Math.round(n);
+        /* PR#8560: n=0 is a valid value */
+        if (n < 0 || p < 0 || p > 1) {
+            return Double.NaN;
+        }
+
+        if (x < 0) {
+            return DPQ.dt0(logP, lowerTail);
+        }
+        x = Math.floor(x + 1e-7);
+        if (n <= x) {
+            return DPQ.dt1(logP, lowerTail);
+        }
+        return Pbeta.pbeta(p, x + 1, n - x, !lowerTail, logP);
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Qbinom.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Qbinom.java
new file mode 100644
index 0000000000..345ad2f096
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Qbinom.java
@@ -0,0 +1,198 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (c) 2000--2009, The R Core Team
+ * Copyright (c) 2003--2009, The R Foundation
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.stats;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.ops.na.NAProfile;
+
+public abstract class Qbinom extends RExternalBuiltinNode.Arg5 {
+
+    private static final class Search {
+        private double z;
+
+        Search(double z) {
+            this.z = z;
+        }
+
+        double doSearch(double initialY, double p, double n, double pr, double incr) {
+            double y = initialY;
+            if (z >= p) {
+                /* search to the left */
+                for (;;) {
+                    double newz;
+                    if (y == 0 || (newz = Pbinom.pbinom(y - incr, n, pr, /* l._t. */true, /* logP */false)) < p) {
+                        return y;
+                    }
+                    y = Math.max(0, y - incr);
+                    z = newz;
+                }
+            } else { /* search to the right */
+                for (;;) {
+                    y = Math.min(y + incr, n);
+                    if (y == n || (z = Pbinom.pbinom(y, n, pr, /* l._t. */true, /* logP */false)) >= p) {
+                        return y;
+                    }
+                }
+            }
+        }
+    }
+
+    @TruffleBoundary
+    private static double qbinom(double initialP, double n, double pr, boolean lowerTail, boolean logP) {
+        double p = initialP;
+
+        if (Double.isNaN(p) || Double.isNaN(n) || Double.isNaN(pr)) {
+            return p + n + pr;
+        }
+
+        if (!Double.isFinite(n) || !Double.isFinite(pr)) {
+            return Double.NaN;
+        }
+        /* if logP is true, p = -Inf is a legitimate value */
+        if (!Double.isFinite(p) && !logP) {
+            return Double.NaN;
+        }
+
+        if (n != Math.floor(n + 0.5)) {
+            return Double.NaN;
+        }
+        if (pr < 0 || pr > 1 || n < 0) {
+            return Double.NaN;
+        }
+
+        if (logP) {
+            if (p > 0) {
+                return Double.NaN;
+            }
+            if (p == 0) {
+                return lowerTail ? n : (double) 0;
+            }
+            if (p == Double.NEGATIVE_INFINITY) {
+                return lowerTail ? (double) 0 : n;
+            }
+        } else { /* !logP */
+            if (p < 0 || p > 1) {
+                return Double.NaN;
+            }
+            if (p == 0) {
+                return lowerTail ? (double) 0 : n;
+            }
+            if (p == 1) {
+                return lowerTail ? n : (double) 0;
+            }
+        }
+
+        if (pr == 0. || n == 0) {
+            return 0.;
+        }
+
+        double q = 1 - pr;
+        if (q == 0.) {
+            /* covers the full range of the distribution */
+            return n;
+        }
+        double mu = n * pr;
+        double sigma = Math.sqrt(n * pr * q);
+        double gamma = (q - pr) / sigma;
+
+        /*
+         * Note : "same" code in qpois.c, qbinom.c, qnbinom.c -- FIXME: This is far from optimal
+         * [cancellation for p ~= 1, etc]:
+         */
+        if (!lowerTail || logP) {
+            p = DPQ.dtQIv(logP, lowerTail, p); /* need check again (cancellation!): */
+            if (p == 0.) {
+                return 0.;
+            }
+            if (p == 1.) {
+                return n;
+            }
+        }
+        /* temporary hack --- FIXME --- */
+        if (p + 1.01 * RRuntime.EPSILON >= 1.) {
+            return n;
+        }
+
+        /* y := approx.value (Cornish-Fisher expansion) : */
+
+        double z = Random2.qnorm5(p, 0., 1., /* lowerTail */true, /* logP */false);
+        double y = Math.floor(mu + sigma * (z + gamma * (z * z - 1) / 6) + 0.5);
+
+        if (y > n) {
+            /* way off */
+            y = n;
+        }
+
+        z = Pbinom.pbinom(y, n, pr, /* lowerTail */true, /* logP */false);
+
+        /* fuzz to ensure left continuity: */
+        p *= 1 - 64 * RRuntime.EPSILON;
+
+        Search search = new Search(z);
+
+        if (n < 1e5) {
+            return search.doSearch(y, p, n, pr, 1);
+        }
+        /* Otherwise be a bit cleverer in the search */
+        double incr = Math.floor(n * 0.001);
+        double oldincr;
+        do {
+            oldincr = incr;
+            y = search.doSearch(y, p, n, pr, incr);
+            incr = Math.max(1, Math.floor(incr / 100));
+        } while (oldincr > 1 && incr > n * 1e-15);
+        return y;
+    }
+
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        casts.toDouble(0).toDouble(1).toDouble(2).firstBoolean(3).firstBoolean(4);
+    }
+
+    @Specialization
+    protected Object qbinom(RAbstractDoubleVector p, RAbstractDoubleVector n, RAbstractDoubleVector pr, boolean lowerTail, boolean logP, //
+                    @Cached("create()") NAProfile na) {
+        int pLength = p.getLength();
+        int nLength = n.getLength();
+        int prLength = pr.getLength();
+        if (pLength == 0 || nLength == 0 || prLength == 0) {
+            return RDataFactory.createEmptyDoubleVector();
+        }
+        int length = Math.max(pLength, Math.max(nLength, prLength));
+        double[] result = new double[length];
+
+        boolean complete = true;
+        boolean nans = false;
+        for (int i = 0; i < length; i++) {
+            double value = qbinom(p.getDataAt(i % pLength), n.getDataAt(i % nLength), pr.getDataAt(i % prLength), lowerTail, logP);
+            if (na.isNA(value)) {
+                complete = false;
+            } else if (Double.isNaN(value)) {
+                nans = true;
+            }
+            result[i] = value;
+        }
+        if (nans) {
+            RError.warning(RError.SHOW_CALLER, RError.Message.NAN_PRODUCED);
+        }
+        return RDataFactory.createDoubleVector(result, complete);
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/TOMS708.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/TOMS708.java
new file mode 100644
index 0000000000..96df94d21b
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/TOMS708.java
@@ -0,0 +1,2354 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 1995-2012, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.stats;
+
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.RInternalError;
+
+import static com.oracle.truffle.r.library.stats.MathConstants.*;
+
+/*
+ * transcribed from toms708.c - as the original file contains no copyright header, we assume that it is copyright R code and R foundation.
+ */
+
+public class TOMS708 {
+
+    @SuppressWarnings("unused")
+    private static void debugPrintf(String format, Object... args) {
+        // System.out.print(String.format(format, args));
+    }
+
+    private static void emitWarning(String format, Object... args) {
+        RError.warning(RError.SHOW_CALLER, Message.GENERIC, String.format(format, args));
+    }
+
+    // R_Log1_Exp
+    public static double log1Exp(double x) {
+        return ((x) > -M_LN2 ? log(-rexpm1(x)) : log1p(-exp(x)));
+    }
+
+    private static double sin(double v) {
+        return Math.sin(v);
+    }
+
+    private static double cos(double v) {
+        return Math.cos(v);
+    }
+
+    private static double log(double v) {
+        return Math.log(v);
+    }
+
+    private static double sqrt(double v) {
+        return Math.sqrt(v);
+    }
+
+    private static double log1p(double v) {
+        return Math.log1p(v);
+    }
+
+    private static double exp(double v) {
+        return Math.exp(v);
+    }
+
+    private static double fabs(double v) {
+        return Math.abs(v);
+    }
+
+    private static double pow(double a, double b) {
+        return Math.pow(a, b);
+    }
+
+    private static double min(double a, double b) {
+        return Math.min(a, b);
+    }
+
+    private static double max(double a, double b) {
+        return Math.max(a, b);
+    }
+
+    private static final double ML_NEGINF = Double.NEGATIVE_INFINITY;
+    private static final int INT_MAX = Integer.MAX_VALUE;
+    private static final double DBL_MIN = Double.MIN_NORMAL;
+    private static final double INV_SQRT_2_PI = .398942280401433; /* == 1/sqrt(2*pi); */
+
+    public static final class Bratio {
+        public double w;
+        public double w1;
+        public int ierr;
+
+        private enum States {
+            Start,
+            L131,
+            L140,
+            L200,
+            L210,
+            L211,
+            L201,
+            L_w_bpser,
+            L_w1_bpser,
+            L_end_from_w1_log,
+            L_end_from_w1,
+            L_end,
+            L_end_from_w,
+            L_bfrac,
+        }
+
+        public void bratio(double a, double b, double x, double y, boolean logP) {
+            boolean doSwap = false;
+            int n = 0;
+            double z = 0;
+            double a0 = 0;
+            double b0 = 0;
+            double x0 = 0;
+            double y0 = 0;
+            double eps = 0;
+            double lambda = 0;
+            States state = States.Start;
+            Bgrat bgrat = new Bgrat();
+            while (true) {
+                switch (state) {
+/*
+ * -----------------------------------------------------------------------
+ * 
+ * Evaluation of the Incomplete Beta function I_x(a,b)
+ * 
+ * --------------------
+ * 
+ * It is assumed that a and b are nonnegative, and that x <= 1 and y = 1 - x. Bratio assigns w and
+ * w1 the values
+ * 
+ * w = I_x(a,b) w1 = 1 - I_x(a,b)
+ * 
+ * ierr is a variable that reports the status of the results. If no input errors are detected then
+ * ierr is set to 0 and w and w1 are computed. otherwise, if an error is detected, then w and w1 are
+ * assigned the value 0 and ierr is set to one of the following values ...
+ * 
+ * ierr = 1 if a or b is negative ierr = 2 if a = b = 0 ierr = 3 if x < 0 or x > 1 ierr = 4 if y < 0
+ * or y > 1 ierr = 5 if x + y != 1 ierr = 6 if x = a = 0 ierr = 7 if y = b = 0 ierr = 8 "error" in
+ * bgrat()
+ * 
+ * -------------------- Written by Alfred H. Morris, Jr. Naval Surface Warfare Center Dahlgren,
+ * Virginia Revised ... Nov 1991
+ * -----------------------------------------------------------------------
+ */
+                    case Start:
+
+/*
+ * eps is a machine dependent constant: the smallest floating point number for which 1.0 + eps > 1.0
+ */
+                        eps = RRuntime.EPSILON; /* == DBL_EPSILON (in R, Rmath) */
+
+/* ----------------------------------------------------------------------- */
+                        w = DPQ.d0(logP);
+                        w1 = DPQ.d0(logP);
+
+                        if (a < 0.0 || b < 0.0) {
+                            ierr = 1;
+                            return;
+                        }
+                        if (a == 0.0 && b == 0.0) {
+                            ierr = 2;
+                            return;
+                        }
+                        if (x < 0.0 || x > 1.0) {
+                            ierr = 3;
+                            return;
+                        }
+                        if (y < 0.0 || y > 1.0) {
+                            ierr = 4;
+                            return;
+                        }
+
+                        /* check that 'y == 1 - x' : */
+                        z = x + y - 0.5 - 0.5;
+
+                        if (Math.abs(z) > eps * 3.0) {
+                            ierr = 5;
+                            return;
+                        }
+                        debugPrintf("bratio(a=%f, b=%f, x=%9f, y=%9f, .., log_p=%b): ",
+                                        a, b, x, y, logP);
+
+                        ierr = 0;
+                        if (x == 0.0) {
+                            state = States.L200;
+                            continue;
+                        }
+                        if (y == 0.0) {
+                            state = States.L210;
+                            continue;
+                        }
+
+                        if (a == 0.0) {
+                            state = States.L211;
+                            continue;
+                        }
+                        if (b == 0.0) {
+                            state = States.L201;
+                            continue;
+                        }
+
+                        eps = Math.max(eps, 1e-15);
+                        boolean aLtB = a < b;
+                        if (/* max(a,b) */(aLtB ? b : a) < eps * .001) { /*
+                                                                          * procedure for a and b <
+                                                                          * 0.001 * eps
+                                                                          */
+                            // L230: -- result *independent* of x (!)
+                            // *w = a/(a+b) and w1 = b/(a+b) :
+                            if (logP) {
+                                if (aLtB) {
+                                    w = Math.log1p(-a / (a + b)); // notably if a << b
+                                    w1 = Math.log(a / (a + b));
+                                } else { // b <= a
+                                    w = Math.log(b / (a + b));
+                                    w1 = Math.log1p(-b / (a + b));
+                                }
+                            } else {
+                                w = b / (a + b);
+                                w1 = a / (a + b);
+                            }
+                            debugPrintf("a & b very small -> simple ratios (%f,%f)\n", w, w1);
+                            return;
+                        }
+
+                        if (Math.min(a, b) <= 1.) { /*------------------------ a <= 1  or  b <= 1 ---- */
+
+                            doSwap = (x > 0.5);
+                            if (doSwap) {
+                                a0 = b;
+                                x0 = y;
+                                b0 = a;
+                                y0 = x;
+                            } else {
+                                a0 = a;
+                                x0 = x;
+                                b0 = b;
+                                y0 = y;
+                            }
+                            /* now have x0 <= 1/2 <= y0 (still x0+y0 == 1) */
+                            debugPrintf(" min(a,b) <= 1, do_swap=%b;", doSwap);
+
+                            if (b0 < Math.min(eps, eps * a0)) { /* L80: */
+                                w = fpser(a0, b0, x0, eps, logP);
+                                w1 = logP ? log1Exp(w) : 0.5 - w + 0.5;
+                                debugPrintf("  b0 small -> w := fpser(*) = %.15f\n", w);
+                                state = States.L_end;
+                                continue;
+                            }
+
+                            if (a0 < Math.min(eps, eps * b0) && b0 * x0 <= 1.0) { /* L90: */
+                                w1 = apser(a0, b0, x0, eps);
+                                debugPrintf("  a0 small -> w1 := apser(*) = %.15fg\n", w1);
+                                state = States.L_end_from_w1;
+                                continue;
+                            }
+
+                            boolean didBup = false;
+                            if (Math.max(a0, b0) > 1.0) {
+                                /* L20: min(a,b) <= 1 < max(a,b) */
+                                debugPrintf("\n L20:  min(a,b) <= 1 < max(a,b); ");
+                                if (b0 <= 1.0) {
+                                    state = States.L_w_bpser;
+                                    continue;
+                                }
+
+                                if (x0 >= 0.29) {
+                                    /* was 0.3, PR#13786 */
+                                    state = States.L_w1_bpser;
+                                    continue;
+                                }
+
+                                if (x0 < 0.1 && Math.pow(x0 * b0, a0) <= 0.7) {
+                                    state = States.L_w_bpser;
+                                    continue;
+                                }
+
+                                if (b0 > 15.0) {
+                                    w1 = 0.;
+                                    state = States.L131;
+                                }
+                            } else { /* a, b <= 1 */
+                                debugPrintf("\n      both a,b <= 1; ");
+                                if (a0 >= Math.min(0.2, b0)) {
+                                    state = States.L_w_bpser;
+                                    continue;
+                                }
+
+                                if (Math.pow(x0, a0) <= 0.9) {
+                                    state = States.L_w_bpser;
+                                    continue;
+                                }
+
+                                if (x0 >= 0.3) {
+                                    state = States.L_w1_bpser;
+                                    continue;
+                                }
+                            }
+                            if (state != States.L131) {
+                                n = 20; /* goto L130; */
+                                w1 = bup(b0, a0, y0, x0, n, eps, false);
+                                debugPrintf("  ... n=20 and *w1 := bup(*) = %.15f; ", w1);
+                                didBup = true;
+
+                                b0 += n;
+                            }
+                            debugPrintf(" L131: bgrat(*, w1=%.15f) ", w1);
+                            bgrat.w = w1;
+                            bgrat.bgrat(b0, a0, y0, x0, 15 * eps, false);
+                            w1 = bgrat.w;
+
+                            if (w1 == 0 || (0 < w1 && w1 < 1e-310)) { // w1=0 or very close:
+                                // "almost surely" from underflow, try more: [2013-03-04]
+                                // FIXME: it is even better to do this in bgrat *directly* at least
+// for the case
+                                // !did_bup, i.e., where *w1 = (0 or -Inf) on entry
+                                if (didBup) { // re-do that part on log scale:
+                                    w1 = bup(b0 - n, a0, y0, x0, n, eps, true);
+                                } else {
+                                    w1 = Double.NEGATIVE_INFINITY; // = 0 on log-scale
+                                }
+                                bgrat.w = w1;
+                                bgrat.bgrat(b0, a0, y0, x0, 15 * eps, false);
+                                w1 = bgrat.w;
+                                if (bgrat.ierr != 0) {
+                                    ierr = 8;
+                                }
+                                state = States.L_end_from_w1_log;
+                                continue;
+                            }
+                            // else
+                            if (bgrat.ierr != 0) {
+                                ierr = 8;
+                            }
+                            if (w1 < 0) {
+                                RError.warning(RError.SHOW_CALLER, Message.GENERIC, String.format("bratio(a=%f, b=%f, x=%f): bgrat() -> w1 = %f",
+                                                a, b, x, w1));
+                            }
+                            state = States.L_end_from_w1;
+                            continue;
+                        } else { /* L30: -------------------- both a, b > 1 {a0 > 1 & b0 > 1} --- */
+
+                            if (a > b) {
+                                lambda = (a + b) * y - b;
+                            } else {
+                                lambda = a - (a + b) * x;
+                            }
+
+                            doSwap = (lambda < 0.0);
+                            if (doSwap) {
+                                lambda = -lambda;
+                                a0 = b;
+                                x0 = y;
+                                b0 = a;
+                                y0 = x;
+                            } else {
+                                a0 = a;
+                                x0 = x;
+                                b0 = b;
+                                y0 = y;
+                            }
+
+                            debugPrintf("  L30:  both  a, b > 1; |lambda| = %f, do_swap = %b\n",
+                                            lambda, doSwap);
+
+                            if (b0 < 40.0) {
+                                debugPrintf("  b0 < 40;");
+                                if (b0 * x0 <= 0.7 || (logP && lambda > 650.)) {
+                                    state = States.L_w_bpser;
+                                    continue;
+                                } else {
+                                    state = States.L140;
+                                    continue;
+                                }
+                            } else if (a0 > b0) { /* ---- a0 > b0 >= 40 ---- */
+                                debugPrintf("  a0 > b0 >= 40;");
+                                if (b0 <= 100.0 || lambda > b0 * 0.03) {
+                                    state = States.L_bfrac;
+                                    continue;
+                                }
+                            } else if (a0 <= 100.0) {
+                                debugPrintf("  a0 <= 100; a0 <= b0 >= 40;");
+                                state = States.L_bfrac;
+                                continue;
+                            } else if (lambda > a0 * 0.03) {
+                                debugPrintf("  b0 >= a0 > 100; lambda > a0 * 0.03 ");
+                                state = States.L_bfrac;
+                                continue;
+                            }
+
+                            /* else if none of the above L180: */
+                            w = basym(a0, b0, lambda, eps * 100.0, logP);
+                            w1 = logP ? log1Exp(w) : 0.5 - w + 0.5;
+                            debugPrintf("  b0 >= a0 > 100; lambda <= a0 * 0.03: *w:= basym(*) =%.15f\n",
+                                            w);
+                            state = States.L_end;
+                            continue;
+
+                        } /* else: a, b > 1 */
+
+/* EVALUATION OF THE APPROPRIATE ALGORITHM */
+
+                    case L_w_bpser: // was L100
+                        w = bpser(a0, b0, x0, eps, logP);
+                        w1 = logP ? log1Exp(w) : 0.5 - w + 0.5;
+                        debugPrintf(" L_w_bpser: *w := bpser(*) = %.1fg\n", w);
+                        state = States.L_end;
+                        continue;
+
+                    case L_w1_bpser:  // was L110
+                        w1 = bpser(b0, a0, y0, eps, logP);
+                        w = logP ? log1Exp(w1) : 0.5 - w1 + 0.5;
+                        debugPrintf(" L_w1_bpser: *w1 := bpser(*) = %.15f\n", w1);
+                        state = States.L_end;
+                        continue;
+
+                    case L_bfrac:
+                        w = bfrac(a0, b0, x0, y0, lambda, eps * 15.0, logP);
+                        w1 = logP ? log1Exp(w) : 0.5 - w + 0.5;
+                        debugPrintf(" L_bfrac: *w := bfrac(*) = %f\n", w);
+                        state = States.L_end;
+                        continue;
+
+                    case L140:
+                        /* b0 := fractional_part( b0 ) in (0, 1] */
+                        n = (int) b0;
+                        b0 -= n;
+                        if (b0 == 0.) {
+                            --n;
+                            b0 = 1.;
+                        }
+
+                        w = bup(b0, a0, y0, x0, n, eps, false);
+
+                        debugPrintf(" L140: *w := bup(b0=%g,..) = %.15f; ", b0, w);
+                        if (w < DBL_MIN && logP) { /* do not believe it; try bpser() : */
+                            /* revert: */b0 += n;
+                            /* which is only valid if b0 <= 1 || b0*x0 <= 0.7 */
+                            state = States.L_w_bpser;
+                            continue;
+                        }
+                        if (x0 <= 0.7) {
+                            /* log_p : TODO: w = bup(.) + bpser(.) -- not so easy to use log-scale */
+                            w += bpser(a0, b0, x0, eps, /* log_p = */false);
+                            debugPrintf(" x0 <= 0.7: *w := *w + bpser(*) = %.15f\n", w);
+                            state = States.L_end_from_w;
+                            continue;
+                        }
+                        /* L150: */
+                        if (a0 <= 15.0) {
+                            n = 20;
+                            w += bup(a0, b0, x0, y0, n, eps, false);
+                            debugPrintf("\n a0 <= 15: *w := *w + bup(*) = %.15f;", w);
+                            a0 += n;
+                        }
+                        debugPrintf(" bgrat(*, w=%.15f) ", w);
+                        bgrat.w = w;
+                        bgrat.bgrat(a0, b0, x0, y0, 15 * eps, false);
+                        w = bgrat.w;
+                        if (bgrat.ierr != 0) {
+                            ierr = 8;
+                        }
+                        state = States.L_end_from_w;
+                        continue;
+
+/* TERMINATION OF THE PROCEDURE */
+
+                    case L200:
+                    case L201:
+                        if (state == States.L200 && a == 0.0) {
+                            ierr = 6;
+                            return;
+                        }
+                        // else:
+                        w = DPQ.d0(logP);
+                        w1 = DPQ.d1(logP);
+                        return;
+
+                    case L210:
+                    case L211:
+                        if (state == States.L210 && b == 0.0) {
+                            ierr = 7;
+                            return;
+                        }
+                        // else:
+                        w = DPQ.d1(logP);
+                        w1 = DPQ.d0(logP);
+                        return;
+
+                    case L_end_from_w:
+                        if (logP) {
+                            w1 = Math.log1p(-w);
+                            w = Math.log(w);
+                        } else {
+                            w1 = 0.5 - w + 0.5;
+                        }
+                        state = States.L_end;
+                        continue;
+
+                    case L_end_from_w1:
+                        if (logP) {
+                            w = Math.log1p(-w1);
+                            w1 = Math.log(w1);
+                        } else {
+                            w = 0.5 - w1 + 0.5;
+                        }
+                        state = States.L_end;
+                        continue;
+
+                    case L_end_from_w1_log:
+                        // *w1 = log(w1) already; w = 1 - w1 ==> log(w) = log(1 - w1) = log(1 -
+// exp(*w1))
+                        if (logP) {
+                            w = log1Exp(w1);
+                        } else {
+                            w = /* 1 - exp(*w1) */-Math.expm1(w1);
+                            w1 = Math.exp(w1);
+                        }
+                        state = States.L_end;
+                        continue;
+
+                    case L_end:
+                        if (doSwap) { /* swap */
+                            double t = w;
+                            w = w1;
+                            w1 = t;
+                        }
+                        return;
+                    default:
+                        throw RInternalError.shouldNotReachHere("state: " + state);
+                }/* bratio */
+            }
+        }
+    }
+
+    private static final class Bgrat {
+        private static final int n_terms_bgrat = 30;
+
+        private int ierr;
+        private double w;
+
+        void bgrat(double a, double b, double x, double y, double eps, boolean logW) {
+/*
+ * ----------------------------------------------------------------------- Asymptotic Expansion for
+ * I_x(a,b) when a is larger than b. Compute w := w + I_x(a,b) It is assumed a >= 15 and b <= 1. eps
+ * is the tolerance used. ierr is a variable that reports the status of the results.
+ *
+ * if(log_w), *w itself must be in log-space; compute w := w + I_x(a,b) but return *w = log(w): *w
+ * := log(exp(*w) + I_x(a,b)) = logspace_add(*w, log( I_x(a,b) ))
+ * -----------------------------------------------------------------------
+ */
+
+            double[] c = new double[n_terms_bgrat];
+            double[] d = new double[n_terms_bgrat];
+            double bm1 = b - 0.5 - 0.5;
+            double nu = a + bm1 * 0.5;
+            // nu = a + (b-1)/2 =: T, in (9.1) of Didonato & Morris(1992), p.362
+            double lnx = (y > 0.375) ? Math.log(x) : alnrel(-y);
+            double z = -nu * lnx;
+            // z =: u in (9.1) of D.&M.(1992)
+
+            if (b * z == 0.0) { // should not happen, but does, e.g.,
+                // for pbeta(1e-320, 1e-5, 0.5) i.e., _subnormal_ x,
+                // Warning ... bgrat(a=20.5, b=1e-05, x=1, y=9.99989e-321): ..
+                RError.warning(RError.SHOW_CALLER, Message.GENERIC, String.format(
+                                "bgrat(a=%f, b=%f, x=%f, y=%f): b*z == 0 underflow, hence inaccurate pbeta()",
+                                a, b, x, y));
+                /* L_Error: THE EXPANSION CANNOT BE COMPUTED */
+                ierr = 1;
+                return;
+            }
+
+/* COMPUTATION OF THE EXPANSION */
+            /*
+             * r1 = b * (gam1(b) + 1.0) * exp(b * log(z)),// = b/gamma(b+1) z^b = z^b / gamma(b) set
+             * r := exp(-z) * z^b / gamma(b) ; gam1(b) = 1/gamma(b+1) - 1 , b in [-1/2, 3/2]
+             */
+            // exp(a*lnx) underflows for large (a * lnx); e.g. large a ==> using log_r := log(r):
+            // r = r1 * exp(a * lnx) * exp(bm1 * 0.5 * lnx);
+            // log(r)=log(b) + log1p(gam1(b)) + b * log(z) + (a * lnx) + (bm1 * 0.5 * lnx),
+            double logR = Math.log(b) + log1p(gam1(b)) + b * Math.log(z) + nu * lnx;
+            // FIXME work with log_u = log(u) also when log_p=FALSE (??)
+            // u is 'factored out' from the expansion {and multiplied back, at the end}:
+            double logU = logR - (algdiv(b, a) + b * Math.log(nu)); // algdiv(b,a) =
+            // log(gamma(a)/gamma(a+b))
+            /* u = (log_p) ? log_r - u : exp(log_r-u); // =: M in (9.2) of {reference above} */
+            /* u = algdiv(b, a) + b * log(nu);// algdiv(b,a) = log(gamma(a)/gamma(a+b)) */
+            // u = (log_p) ? log_u : exp(log_u); // =: M in (9.2) of {reference above}
+            double u = Math.exp(logU);
+
+            if (logU == Double.NEGATIVE_INFINITY) {
+                /* L_Error: THE EXPANSION CANNOT BE COMPUTED */ierr = 2;
+                return;
+            }
+
+            boolean u0 = (u == 0.); // underflow --> do work with log(u) == log_u !
+            double l = // := *w/u .. but with care: such that it also works when u underflows to 0:
+            logW ? ((w == Double.NEGATIVE_INFINITY) ? 0. : Math.exp(w - logU)) : ((w == 0.) ? 0. : Math.exp(Math.log(w) - logU));
+
+            debugPrintf(" bgrat(a=%f, b=%f, x=%f, *)\n -> u=%f, l='w/u'=%f, ", a, b, x, u, l);
+
+            double qR = gratR(b, z, logR, eps); // = q/r of former grat1(b,z, r, &p, &q)
+            double v = 0.25 / (nu * nu);
+            double t2 = lnx * 0.25 * lnx;
+            double j = qR;
+            double sum = j;
+            double t = 1.0;
+            double cn = 1.0;
+            double n2 = 0.;
+            for (int n = 1; n <= n_terms_bgrat; ++n) {
+                double bp2n = b + n2;
+                j = (bp2n * (bp2n + 1.0) * j + (z + bp2n + 1.0) * t) * v;
+                n2 += 2.;
+                t *= t2;
+                cn /= n2 * (n2 + 1.);
+                int nm1 = n - 1;
+                c[nm1] = cn;
+                double s = 0.0;
+                if (n > 1) {
+                    double coef = b - n;
+                    for (int i = 1; i <= nm1; ++i) {
+                        s += coef * c[i - 1] * d[nm1 - i];
+                        coef += b;
+                    }
+                }
+                d[nm1] = bm1 * cn + s / n;
+                double dj = d[nm1] * j;
+                sum += dj;
+                if (sum <= 0.0) {
+                    /* L_Error: THE EXPANSION CANNOT BE COMPUTED */ierr = 3;
+                    return;
+                }
+                if (Math.abs(dj) <= eps * (sum + l)) {
+                    break;
+                } else if (n == n_terms_bgrat) {
+                    // never? ; please notify R-core if seen:
+                    RError.warning(RError.SHOW_CALLER, Message.GENERIC, String.format("bgrat(a=%f, b=%f, x=%f,..): did *not* converge; dj=%f, rel.err=%f\n", a, b, x, dj, Math.abs(dj) / (sum + l)));
+                }
+            }
+
+/* ADD THE RESULTS TO W */
+            ierr = 0;
+            if (logW) {
+                // *w is in log space already:
+                w = logspaceAdd(w, logU + Math.log(sum));
+            } else {
+                w += (u0 ? Math.exp(logU + Math.log(sum)) : u * sum);
+            }
+            return;
+        } /* bgrat */
+    }
+
+    private static double fpser(double a, double b, double x, double eps, boolean logP) {
+/*
+ * ----------------------------------------------------------------------- *
+ * 
+ * EVALUATION OF I (A,B) X
+ * 
+ * FOR B < MIN(EPS, EPS*A) AND X <= 0.5
+ * 
+ * -----------------------------------------------------------------------
+ */
+
+        double ans;
+
+        /* SET ans := x^a : */
+        if (logP) {
+            ans = a * log(x);
+        } else if (a > eps * 0.001) {
+            double t = a * log(x);
+            if (t < exparg(1)) { /* exp(t) would underflow */
+                return 0.0;
+            }
+            ans = exp(t);
+        } else {
+            ans = 1.;
+        }
+
+/* NOTE THAT 1/B(A,B) = B */
+
+        if (logP) {
+            ans += log(b) - log(a);
+        } else {
+            ans *= b / a;
+        }
+
+        double tol = eps / a;
+        double an = a + 1.0;
+        double t = x;
+        double s = t / an;
+        double c;
+        do {
+            an += 1.0;
+            t = x * t;
+            c = t / an;
+            s += c;
+        } while (fabs(c) > tol);
+
+        if (logP) {
+            ans += log1p(a * s);
+        } else {
+            ans *= a * s + 1.0;
+        }
+        return ans;
+    } /* fpser */
+
+    static double apser(double a, double b, double x, double eps) {
+/*
+ * ----------------------------------------------------------------------- apser() yields the
+ * incomplete beta ratio I_{1-x}(b,a) for a <= min(eps,eps*b), b*x <= 1, and x <= 0.5, i.e., a is
+ * very small. Use only if above inequalities are satisfied.
+ * -----------------------------------------------------------------------
+ */
+
+        double g = .577215664901533;
+
+        double bx = b * x;
+
+        double t = x - bx;
+        double c;
+        if (b * eps <= 0.02) {
+            c = log(x) + psi(b) + g + t;
+        } else {
+            c = log(bx) + g + t;
+        }
+
+        double tol = eps * 5.0 * fabs(c);
+        double j = 1.;
+        double s = 0.;
+        double aj;
+        do {
+            j += 1.0;
+            t *= x - bx / j;
+            aj = t / j;
+            s += aj;
+        } while (fabs(aj) > tol);
+
+        return -a * (c + s);
+    } /* apser */
+
+    static double bpser(double a, double b, double x, double eps, boolean logP) {
+/*
+ * ----------------------------------------------------------------------- Power SERies expansion
+ * for evaluating I_x(a,b) when b <= 1 or b*x <= 0.7. eps is the tolerance used.
+ * -----------------------------------------------------------------------
+ */
+
+        if (x == 0.) {
+            return DPQ.d0(logP);
+        }
+/* ----------------------------------------------------------------------- */
+/* compute the factor x^a/(a*Beta(a,b)) */
+/* ----------------------------------------------------------------------- */
+        double ans;
+        double a0 = min(a, b);
+        if (a0 >= 1.0) { /* ------ 1 <= a0 <= b0 ------ */
+            double z = a * log(x) - betaln(a, b);
+            ans = logP ? z - log(a) : exp(z) / a;
+        } else {
+            double b0 = max(a, b);
+
+            if (b0 < 8.0) {
+
+                if (b0 <= 1.0) { /* ------ a0 < 1 and b0 <= 1 ------ */
+
+                    if (logP) {
+                        ans = a * log(x);
+                    } else {
+                        ans = pow(x, a);
+                        if (ans == 0.) {
+                            /* once underflow, always underflow .. */
+                            return ans;
+                        }
+                    }
+                    double apb = a + b;
+                    double z;
+                    if (apb > 1.0) {
+                        double u = a + b - 1.;
+                        z = (gam1(u) + 1.0) / apb;
+                    } else {
+                        z = gam1(apb) + 1.0;
+                    }
+                    double c = (gam1(a) + 1.0) * (gam1(b) + 1.0) / z;
+
+                    if (logP) {
+                        /* FIXME ? -- improve quite a bit for c ~= 1 */
+                        ans += log(c * (b / apb));
+                    } else {
+                        ans *= c * (b / apb);
+                    }
+
+                } else { /* ------ a0 < 1 < b0 < 8 ------ */
+
+                    double u = gamln1(a0);
+                    int m = (int) (b0 - 1.0);
+                    if (m >= 1) {
+                        double c = 1.0;
+                        for (int i = 1; i <= m; ++i) {
+                            b0 += -1.0;
+                            c *= b0 / (a0 + b0);
+                        }
+                        u += log(c);
+                    }
+
+                    double z = a * log(x) - u;
+                    b0 += -1.0; // => b0 in (0, 7)
+                    double apb = a0 + b0;
+                    double t;
+                    if (apb > 1.0) {
+                        u = a0 + b0 - 1.;
+                        t = (gam1(u) + 1.0) / apb;
+                    } else {
+                        t = gam1(apb) + 1.0;
+                    }
+
+                    if (logP) {
+                        /* FIXME? potential for improving log(t) */
+                        ans = z + log(a0 / a) + log1p(gam1(b0)) - log(t);
+                    } else {
+                        ans = exp(z) * (a0 / a) * (gam1(b0) + 1.0) / t;
+                    }
+                }
+
+            } else { /* ------ a0 < 1 < 8 <= b0 ------ */
+
+                double u = gamln1(a0) + algdiv(a0, b0);
+                double z = a * log(x) - u;
+
+                if (logP) {
+                    ans = z + log(a0 / a);
+                } else {
+                    ans = a0 / a * exp(z);
+                }
+            }
+        }
+        debugPrintf(" bpser(a=%f, b=%f, x=%f, log=%b): prelim.ans = %.14f;\n", a, b, x, logP, ans);
+        if (ans == DPQ.d0(logP) || (!logP && a <= eps * 0.1)) {
+            return ans;
+        }
+
+/* ----------------------------------------------------------------------- */
+/* COMPUTE THE SERIES */
+/* ----------------------------------------------------------------------- */
+        double sum = 0.;
+        double n = 0.;
+        double c = 1.;
+        double tol = eps / a;
+
+        double w;
+        do {
+            n += 1.;
+            c *= (0.5 - b / n + 0.5) * x;
+            w = c / (a + n);
+            sum += w;
+        } while (n < 1e7 && fabs(w) > tol);
+        if (fabs(w) > tol) { // the series did not converge (in time)
+            // warn only when the result seems to matter:
+            if ((logP && !(a * sum > -1. && fabs(log1p(a * sum)) < eps * fabs(ans))) || (!logP && fabs(a * sum + 1) != 1.)) {
+                emitWarning(" bpser(a=%f, b=%f, x=%f,...) did not converge (n=1e7, |w|/tol=%f > 1; A=%f)", a, b, x, fabs(w) / tol, ans);
+            }
+        }
+        debugPrintf("  -> n=%d iterations, |w|=%f %s %f=tol:=eps/a ==> a*sum=%f\n", (int) n, fabs(w), (fabs(w) > tol) ? ">!!>" : "<=", tol, a * sum);
+        if (logP) {
+            if (a * sum > -1.0) {
+                ans += log1p(a * sum);
+            } else {
+                ans = ML_NEGINF;
+            }
+        } else {
+            ans *= a * sum + 1.0;
+        }
+        return ans;
+    } /* bpser */
+
+    static double bup(double a, double b, double x, double y, int n, double eps, boolean giveLog) {
+/* ----------------------------------------------------------------------- */
+/* EVALUATION OF I_x(A,B) - I_x(A+N,B) WHERE N IS A POSITIVE INT. */
+/* EPS IS THE TOLERANCE USED. */
+/* ----------------------------------------------------------------------- */
+
+// Obtain the scaling factor exp(-mu) and exp(mu)*(x^a * y^b / beta(a,b))/a
+
+        double apb = a + b;
+        double ap1 = a + 1.0;
+        int mu;
+        double d;
+        if (n > 1 && a >= 1. && apb >= ap1 * 1.1) {
+            mu = (int) fabs(exparg(1));
+            int k = (int) exparg(0);
+            if (mu > k) {
+                mu = k;
+            }
+            double t = mu;
+            d = exp(-t);
+        } else {
+            mu = 0;
+            d = 1.0;
+        }
+
+        /* L10: */
+        double retVal = giveLog ? brcmp1(mu, a, b, x, y, true) - log(a) : brcmp1(mu, a, b, x, y, false) / a;
+        if (n == 1 || (giveLog && retVal == ML_NEGINF) || (!giveLog && retVal == 0.)) {
+            return retVal;
+        }
+
+        int nm1 = n - 1;
+        double w = d;
+
+/* LET K BE THE INDEX OF THE MAXIMUM TERM */
+
+        boolean doL40 = false;
+        int k = 0;
+        do {
+            if (b <= 1.0) {
+                doL40 = true;
+                break;
+            }
+            if (y > 1e-4) {
+                double r = (b - 1.0) * x / y - a;
+                if (r < 1.0) {
+                    doL40 = true;
+                    break;
+                }
+                k = nm1;
+                double t = nm1;
+                if (r < t) {
+                    k = (int) r;
+                }
+            } else {
+                k = nm1;
+            }
+
+/* ADD THE INCREASING TERMS OF THE SERIES */
+
+/* L30: */
+            for (int i = 1; i <= k; ++i) {
+                double l = i - 1;
+                d = (apb + l) / (ap1 + l) * x * d;
+                w += d;
+                /* L31: */
+            }
+            if (k != nm1) {
+                doL40 = true;
+                break;
+            }
+        } while (false);
+        /* ADD THE REMAINING TERMS OF THE SERIES */
+        if (doL40) {
+            for (int i = k + 1; i <= nm1; ++i) {
+                double l = i - 1;
+                d = (apb + l) / (ap1 + l) * x * d;
+                w += d;
+                if (d <= eps * w) {
+                    /* relativ convergence (eps) */
+                    break;
+                }
+            }
+        }
+
+        // L50: TERMINATE THE PROCEDURE
+        if (giveLog) {
+            retVal += log(w);
+        } else {
+            retVal *= w;
+        }
+
+        return retVal;
+    } /* bup */
+
+    static double bfrac(double a, double b, double x, double y, double lambda, double eps, boolean logP) {
+/*
+ * ----------------------------------------------------------------------- Continued fraction
+ * expansion for I_x(a,b) when a, b > 1. It is assumed that lambda = (a + b)*y - b.
+ * -----------------------------------------------------------------------
+ */
+
+        double brc = brcomp(a, b, x, y, logP);
+
+        if (!logP && brc == 0.) {
+            /* already underflowed to 0 */
+            return 0.;
+        }
+
+        double c = lambda + 1.0;
+        double c0 = b / a;
+        double c1 = 1.0 / a + 1.0;
+        double yp1 = y + 1.0;
+
+        double n = 0.0;
+        double p = 1.0;
+        double s = a + 1.0;
+        double an = 0.0;
+        double bn = 1.0;
+        double anp1 = 1.0;
+        double bnp1 = c / c1;
+        double r = c1 / c;
+
+/* CONTINUED FRACTION CALCULATION */
+
+        do {
+            n += 1.0;
+            double t = n / a;
+            double w = n * (b - n) * x;
+            double e = a / s;
+            double alpha = p * (p + c0) * e * e * (w * x);
+            e = (t + 1.0) / (c1 + t + t);
+            double beta = n + w / s + e * (c + n * yp1);
+            p = t + 1.0;
+            s += 2.0;
+
+            /* update an, bn, anp1, and bnp1 */
+
+            t = alpha * an + beta * anp1;
+            an = anp1;
+            anp1 = t;
+            t = alpha * bn + beta * bnp1;
+            bn = bnp1;
+            bnp1 = t;
+
+            double r0 = r;
+            r = anp1 / bnp1;
+            if (fabs(r - r0) <= eps * r) {
+                break;
+            }
+
+            /* rescale an, bn, anp1, and bnp1 */
+
+            an /= bnp1;
+            bn /= bnp1;
+            anp1 = r;
+            bnp1 = 1.0;
+        } while (true);
+
+        return (logP ? brc + log(r) : brc * r);
+    } /* bfrac */
+
+    static double brcomp(double a, double b, double x, double y, boolean logP) {
+/*
+ * ----------------------------------------------------------------------- Evaluation of x^a * y^b /
+ * Beta(a,b) -----------------------------------------------------------------------
+ */
+
+        /* R has M_1_SQRT_2PI , and M_LN_SQRT_2PI = ln(sqrt(2*pi)) = 0.918938.. */
+
+        if (x == 0.0 || y == 0.0) {
+            return DPQ.d0(logP);
+        }
+        double a0 = min(a, b);
+        if (a0 < 8.0) {
+            double lnx;
+            double lny;
+            if (x <= .375) {
+                lnx = log(x);
+                lny = alnrel(-x);
+            } else {
+                if (y > .375) {
+                    lnx = log(x);
+                    lny = log(y);
+                } else {
+                    lnx = alnrel(-y);
+                    lny = log(y);
+                }
+            }
+
+            double z = a * lnx + b * lny;
+            if (a0 >= 1.) {
+                z -= betaln(a, b);
+                return DPQ.dExp(z, logP);
+            }
+
+/* ----------------------------------------------------------------------- */
+/* PROCEDURE FOR a < 1 OR b < 1 */
+/* ----------------------------------------------------------------------- */
+
+            double b0 = max(a, b);
+            if (b0 >= 8.0) { /* L80: */
+                double u = gamln1(a0) + algdiv(a0, b0);
+
+                return (logP ? log(a0) + (z - u) : a0 * exp(z - u));
+            }
+            /* else : */
+
+            if (b0 <= 1.0) { /* algorithm for max(a,b) = b0 <= 1 */
+
+                double eZ = DPQ.dExp(z, logP);
+
+                if (!logP && eZ == 0.0) {
+                    /* exp() underflow */
+                    return 0.;
+                }
+
+                double apb = a + b;
+                if (apb > 1.0) {
+                    double u = a + b - 1.;
+                    z = (gam1(u) + 1.0) / apb;
+                } else {
+                    z = gam1(apb) + 1.0;
+                }
+
+                double c = (gam1(a) + 1.0) * (gam1(b) + 1.0) / z;
+                /* FIXME? log(a0*c)= log(a0)+ log(c) and that is improvable */
+                return (logP ? eZ + log(a0 * c) - log1p(a0 / b0) : eZ * (a0 * c) / (a0 / b0 + 1.0));
+            }
+
+            /* else : ALGORITHM FOR 1 < b0 < 8 */
+
+            double u = gamln1(a0);
+            int n = (int) (b0 - 1.0);
+            if (n >= 1) {
+                double c = 1.0;
+                for (int i = 1; i <= n; ++i) {
+                    b0 += -1.0;
+                    c *= b0 / (a0 + b0);
+                }
+                u = log(c) + u;
+            }
+            z -= u;
+            b0 += -1.0;
+            double apb = a0 + b0;
+            double t;
+            if (apb > 1.0) {
+                u = a0 + b0 - 1.;
+                t = (gam1(u) + 1.0) / apb;
+            } else {
+                t = gam1(apb) + 1.0;
+            }
+
+            return (logP ? log(a0) + z + log1p(gam1(b0)) - log(t) : a0 * exp(z) * (gam1(b0) + 1.0) / t);
+
+        } else {
+/* ----------------------------------------------------------------------- */
+/* PROCEDURE FOR A >= 8 AND B >= 8 */
+/* ----------------------------------------------------------------------- */
+            double h;
+            double x0;
+            double y0;
+            double lambda;
+            if (a <= b) {
+                h = a / b;
+                x0 = h / (h + 1.0);
+                y0 = 1.0 / (h + 1.0);
+                lambda = a - (a + b) * x;
+            } else {
+                h = b / a;
+                x0 = 1.0 / (h + 1.0);
+                y0 = h / (h + 1.0);
+                lambda = (a + b) * y - b;
+            }
+
+            double e = -lambda / a;
+            double u;
+            if (fabs(e) > .6) {
+                u = e - log(x / x0);
+            } else {
+                u = rlog1(e);
+            }
+
+            e = lambda / b;
+            double v;
+            if (fabs(e) <= .6) {
+                v = rlog1(e);
+            } else {
+                v = e - log(y / y0);
+            }
+
+            double z = logP ? -(a * u + b * v) : exp(-(a * u + b * v));
+
+            return (logP ? -M_LN_SQRT_2PI + .5 * log(b * x0) + z - bcorr(a, b) : INV_SQRT_2_PI * sqrt(b * x0) * z * exp(-bcorr(a, b)));
+        }
+    } /* brcomp */
+
+// called only once from bup(), as r = brcmp1(mu, a, b, x, y, false) / a;
+// -----
+    static double brcmp1(int mu, double a, double b, double x, double y, boolean giveLog) {
+/*
+ * ----------------------------------------------------------------------- Evaluation of exp(mu) *
+ * x^a * y^b / beta(a,b) -----------------------------------------------------------------------
+ */
+
+        /* R has M_1_SQRT_2PI */
+
+        /* Local variables */
+
+        double a0 = min(a, b);
+        if (a0 < 8.0) {
+            double lnx;
+            double lny;
+            if (x <= .375) {
+                lnx = log(x);
+                lny = alnrel(-x);
+            } else if (y > .375) {
+                // L11:
+                lnx = log(x);
+                lny = log(y);
+            } else {
+                lnx = alnrel(-y);
+                lny = log(y);
+            }
+
+            // L20:
+            double z = a * lnx + b * lny;
+            if (a0 >= 1.0) {
+                z -= betaln(a, b);
+                return esum(mu, z, giveLog);
+            }
+            // else :
+            /* ----------------------------------------------------------------------- */
+            /* PROCEDURE FOR A < 1 OR B < 1 */
+            /* ----------------------------------------------------------------------- */
+            // L30:
+            double b0 = max(a, b);
+            if (b0 >= 8.0) {
+                /* L80: ALGORITHM FOR b0 >= 8 */
+                double u = gamln1(a0) + algdiv(a0, b0);
+                debugPrintf(" brcmp1(mu,a,b,*): a0 < 1, b0 >= 8;  z=%.15f\n", z);
+                return giveLog ? log(a0) + esum(mu, z - u, true) : a0 * esum(mu, z - u, false);
+
+            } else if (b0 <= 1.0) {
+                // a0 < 1, b0 <= 1
+                double ans = esum(mu, z, giveLog);
+                if (ans == (giveLog ? ML_NEGINF : 0.)) {
+                    return ans;
+                }
+
+                double apb = a + b;
+                if (apb > 1.0) {
+                    // L40:
+                    double u = a + b - 1.;
+                    z = (gam1(u) + 1.0) / apb;
+                } else {
+                    z = gam1(apb) + 1.0;
+                }
+                // L50:
+                double c = giveLog ? log1p(gam1(a)) + log1p(gam1(b)) - log(z) : (gam1(a) + 1.0) * (gam1(b) + 1.0) / z;
+                debugPrintf(" brcmp1(mu,a,b,*): a0 < 1, b0 <= 1;  c=%.15f\n", c);
+                return giveLog ? ans + log(a0) + c - log1p(a0 / b0) : ans * (a0 * c) / (a0 / b0 + 1.0);
+            }
+            // else: algorithm for a0 < 1 < b0 < 8
+            // L60:
+            double u = gamln1(a0);
+            int n = (int) (b0 - 1.0);
+            if (n >= 1) {
+                double c = 1.0;
+                for (int i = 1; i <= n; ++i) {
+                    b0 += -1.0;
+                    c *= b0 / (a0 + b0);
+                    /* L61: */
+                }
+                u += log(c); // TODO?: log(c) = log( prod(...) ) = sum( log(...) )
+            }
+            // L70:
+            z -= u;
+            b0 += -1.0;
+            double apb = a0 + b0;
+            double t;
+            if (apb > 1.) {
+                // L71:
+                t = (gam1(apb - 1.) + 1.0) / apb;
+            } else {
+                t = gam1(apb) + 1.0;
+            }
+            debugPrintf(" brcmp1(mu,a,b,*): a0 < 1 < b0 < 8;  t=%.15f\n", t);
+            // L72:
+            return giveLog ? log(a0) + esum(mu, z, true) + log1p(gam1(b0)) - log(t) : a0 * esum(mu, z, false) * (gam1(b0) + 1.0) / t;
+
+        } else {
+
+/* ----------------------------------------------------------------------- */
+/* PROCEDURE FOR A >= 8 AND B >= 8 */
+/* ----------------------------------------------------------------------- */
+            // L100:
+            double h;
+            double x0;
+            double y0;
+            double lambda;
+            if (a > b) {
+                // L101:
+                h = b / a;
+                x0 = 1.0 / (h + 1.0); // => lx0 := log(x0) = 0 - log1p(h)
+                y0 = h / (h + 1.0);
+                lambda = (a + b) * y - b;
+            } else {
+                h = a / b;
+                x0 = h / (h + 1.0);  // => lx0 := log(x0) = - log1p(1/h)
+                y0 = 1.0 / (h + 1.0);
+                lambda = a - (a + b) * x;
+            }
+            double lx0 = -log1p(b / a); // in both cases
+
+            debugPrintf(" brcmp1(mu,a,b,*): a,b >= 8;  x0=%.15f, lx0=log(x0)=%.15f\n", x0, lx0);
+            // L110:
+            double e = -lambda / a;
+            double u;
+            if (fabs(e) > 0.6) {
+                // L111:
+                u = e - log(x / x0);
+            } else {
+                u = rlog1(e);
+            }
+
+            // L120:
+            e = lambda / b;
+            double v;
+            if (fabs(e) > 0.6) {
+                // L121:
+                v = e - log(y / y0);
+            } else {
+                v = rlog1(e);
+            }
+
+            // L130:
+            double z = esum(mu, -(a * u + b * v), giveLog);
+            return giveLog ? log(INV_SQRT_2_PI) + (log(b) + lx0) / 2. + z - bcorr(a, b) : INV_SQRT_2_PI * sqrt(b * x0) * z * exp(-bcorr(a, b));
+        }
+
+    } /* brcmp1 */
+
+// called only from bgrat() , as q_r = grat_r(b, z, log_r, eps) :
+    static double gratR(double a, double x, double logR, double eps) {
+/*
+ * ----------------------------------------------------------------------- Scaled complement of
+ * incomplete gamma ratio function grat_r(a,x,r) := Q(a,x) / r where Q(a,x) = pgamma(x,a,
+ * lower.tail=false) and r = e^(-x)* x^a / Gamma(a) == exp(log_r)
+ * 
+ * It is assumed that a <= 1. eps is the tolerance to be used.
+ * -----------------------------------------------------------------------
+ */
+
+        if (a * x == 0.0) { /* L130: */
+            if (x <= a) {
+                /* L100: */
+                return exp(-logR);
+            } else {
+                /* L110: */
+                return 0.;
+            }
+        } else if (a == 0.5) { // e.g. when called from pt()
+            /* L120: */
+            if (x < 0.25) {
+                double p = erf(sqrt(x));
+                debugPrintf(" grat_r(a=%f, x=%f ..)): a=1/2 --> p=erf__(.)= %f\n", a, x, p);
+                return (0.5 - p + 0.5) * exp(-logR);
+
+            } else { // 2013-02-27: improvement for "large" x: direct computation of q/r:
+                double sx = sqrt(x);
+                double qR = erfc1(1, sx) / sx * M_SQRT_PI;
+                debugPrintf(" grat_r(a=%f, x=%f ..)): a=1/2 --> q_r=erfc1(..)/r= %f\n", a, x, qR);
+                return qR;
+            }
+        } else if (x < 1.1) { /* L10: Taylor series for P(a,x)/x^a */
+
+            double an = 3.;
+            double c = x;
+            double sum = x / (a + 3.0);
+            double tol = eps * 0.1 / (a + 1.0);
+            double t;
+            do {
+                an += 1.;
+                c *= -(x / an);
+                t = c / (a + an);
+                sum += t;
+            } while (fabs(t) > tol);
+
+            debugPrintf(" grat_r(a=%f, x=%f, log_r=%f): sum=%f; Taylor w/ %.0f terms", a, x, logR, sum, an - 3.);
+            double j = a * x * ((sum / 6. - 0.5 / (a + 2.)) * x + 1. / (a + 1.));
+            double z = a * log(x);
+            double h = gam1(a);
+            double g = h + 1.0;
+
+            if ((x >= 0.25 && (a < x / 2.59)) || (z > -0.13394)) {
+                // L40:
+                double l = rexpm1(z);
+                double q = ((l + 0.5 + 0.5) * j - l) * g - h;
+                if (q <= 0.0) {
+                    debugPrintf(" => q_r= 0.\n");
+                    /* L110: */
+                    return 0.;
+                } else {
+                    debugPrintf(" => q_r=%.15f\n", q * exp(-logR));
+                    return q * exp(-logR);
+                }
+
+            } else {
+                double p = exp(z) * g * (0.5 - j + 0.5);
+                debugPrintf(" => q_r=%.15f\n", (0.5 - p + 0.5) * exp(-logR));
+                return /* q/r = */(0.5 - p + 0.5) * exp(-logR);
+            }
+
+        } else {
+            /* L50: ---- (x >= 1.1) ---- Continued Fraction Expansion */
+
+            double a2n1 = 1.0;
+            double a2n = 1.0;
+            double b2n1 = x;
+            double b2n = x + (1.0 - a);
+            double c = 1.;
+            double am0;
+            double an0;
+
+            do {
+                a2n1 = x * a2n + c * a2n1;
+                b2n1 = x * b2n + c * b2n1;
+                am0 = a2n1 / b2n1;
+                c += 1.;
+                double cA = c - a;
+                a2n = a2n1 + cA * a2n;
+                b2n = b2n1 + cA * b2n;
+                an0 = a2n / b2n;
+            } while (fabs(an0 - am0) >= eps * an0);
+
+            debugPrintf(" grat_r(a=%f, x=%f, log_r=%f): Cont.frac. %.0f terms => q_r=%.15f\n", a, x, logR, c - 1., an0);
+            return /* q/r = (r * an0)/r = */an0;
+        }
+    } /* grat_r */
+
+    private static final int num_IT = 20;
+
+    static double basym(double a, double b, double lambda, double eps, boolean logP) {
+/* ----------------------------------------------------------------------- */
+/* ASYMPTOTIC EXPANSION FOR I_x(A,B) FOR LARGE A AND B. */
+/* LAMBDA = (A + B)*Y - B AND EPS IS THE TOLERANCE USED. */
+/* IT IS ASSUMED THAT LAMBDA IS NONNEGATIVE AND THAT */
+/* A AND B ARE GREATER THAN OR EQUAL TO 15. */
+/* ----------------------------------------------------------------------- */
+
+/* ------------------------ */
+/* ****** NUM IS THE MAXIMUM VALUE THAT N CAN TAKE IN THE DO LOOP */
+/* ENDING AT STATEMENT 50. IT IS REQUIRED THAT NUM BE EVEN. */
+/* THE ARRAYS A0, B0, C, D HAVE DIMENSION NUM + 1. */
+
+        double e0 = 1.12837916709551; // e0 == 2/sqrt(pi)
+        double e1 = .353553390593274; // e1 == 2^(-3/2)
+        double lnE0 = 0.120782237635245; // == ln(e0)
+
+        double[] a0 = new double[num_IT + 1];
+        double[] b0 = new double[num_IT + 1];
+        double[] c = new double[num_IT + 1];
+        double[] d = new double[num_IT + 1];
+
+        double f = a * rlog1(-lambda / a) + b * rlog1(lambda / b);
+        double t;
+        if (logP) {
+            t = -f;
+        } else {
+            t = exp(-f);
+            if (t == 0.0) {
+                return 0; /* once underflow, always underflow .. */
+            }
+        }
+        double z0 = sqrt(f);
+        double z = z0 / e1 * 0.5;
+        double z2 = f + f;
+
+        double h;
+        double r0;
+        double r1;
+        double w0;
+
+        if (a < b) {
+            h = a / b;
+            r0 = 1.0 / (h + 1.0);
+            r1 = (b - a) / b;
+            w0 = 1.0 / sqrt(a * (h + 1.0));
+        } else {
+            h = b / a;
+            r0 = 1.0 / (h + 1.0);
+            r1 = (b - a) / a;
+            w0 = 1.0 / sqrt(b * (h + 1.0));
+        }
+
+        a0[0] = r1 * .66666666666666663;
+        c[0] = a0[0] * -0.5;
+        d[0] = -c[0];
+        double j0 = 0.5 / e0 * erfc1(1, z0);
+        double j1 = e1;
+        double sum = j0 + d[0] * w0 * j1;
+
+        double s = 1.0;
+        double h2 = h * h;
+        double hn = 1.0;
+        double w = w0;
+        double znm1 = z;
+        double zn = z2;
+        for (int n = 2; n <= num_IT; n += 2) {
+            hn *= h2;
+            a0[n - 1] = r0 * 2.0 * (h * hn + 1.0) / (n + 2.0);
+            int np1 = n + 1;
+            s += hn;
+            a0[np1 - 1] = r1 * 2.0 * s / (n + 3.0);
+
+            for (int i = n; i <= np1; ++i) {
+                double r = (i + 1.0) * -0.5;
+                b0[0] = r * a0[0];
+                for (int m = 2; m <= i; ++m) {
+                    double bsum = 0.0;
+                    for (int j = 1; j <= m - 1; ++j) {
+                        int mmj = m - j;
+                        bsum += (j * r - mmj) * a0[j - 1] * b0[mmj - 1];
+                    }
+                    b0[m - 1] = r * a0[m - 1] + bsum / m;
+                }
+                c[i - 1] = b0[i - 1] / (i + 1.0);
+
+                double dsum = 0.0;
+                for (int j = 1; j <= i - 1; ++j) {
+                    dsum += d[i - j - 1] * c[j - 1];
+                }
+                d[i - 1] = -(dsum + c[i - 1]);
+            }
+
+            j0 = e1 * znm1 + (n - 1.0) * j0;
+            j1 = e1 * zn + n * j1;
+            znm1 = z2 * znm1;
+            zn = z2 * zn;
+            w *= w0;
+            double t0 = d[n - 1] * w * j0;
+            w *= w0;
+            double t1 = d[np1 - 1] * w * j1;
+            sum += t0 + t1;
+            if (fabs(t0) + fabs(t1) <= eps * sum) {
+                break;
+            }
+        }
+
+        if (logP) {
+            return lnE0 + t - bcorr(a, b) + log(sum);
+        } else {
+            double u = exp(-bcorr(a, b));
+            return e0 * t * u * sum;
+        }
+
+    } /* basym_ */
+
+    static double exparg(int l) {
+/* -------------------------------------------------------------------- */
+/*
+ * IF L = 0 THEN EXPARG(L) = THE LARGEST POSITIVE W FOR WHICH EXP(W) CAN BE COMPUTED. ==> exparg(0)
+ * = 709.7827 nowadays.
+ */
+
+/*
+ * IF L IS NONZERO THEN EXPARG(L) = THE LARGEST NEGATIVE W FOR WHICH THE COMPUTED VALUE OF EXP(W) IS
+ * NONZERO. ==> exparg(1) = -708.3964 nowadays.
+ */
+
+/* Note... only an approximate value for exparg(L) is needed. */
+/* -------------------------------------------------------------------- */
+
+        double lnb = .69314718055995;
+        int m = (l == 0) ? MathInit.i1mach(16) : MathInit.i1mach(15) - 1;
+
+        return m * lnb * .99999;
+    } /* exparg */
+
+    static double esum(int mu, double x, boolean giveLog) {
+/* ----------------------------------------------------------------------- */
+/* EVALUATION OF EXP(MU + X) */
+/* ----------------------------------------------------------------------- */
+
+        if (giveLog) {
+            return x + mu;
+        }
+
+        // else :
+        double w;
+        if (x > 0.0) { /* L10: */
+            if (mu > 0) {
+                return exp(mu) * exp(x);
+            }
+            w = mu + x;
+            if (w < 0.0) {
+                return exp(mu) * exp(x);
+            }
+        } else { /* x <= 0 */
+            if (mu < 0) {
+                return exp(mu) * exp(x);
+            }
+            w = mu + x;
+            if (w > 0.0) {
+                return exp(mu) * exp(x);
+            }
+        }
+        return exp(w);
+    } /* esum */
+
+    private static double rexpm1(double x) {
+/* ----------------------------------------------------------------------- */
+/* EVALUATION OF THE FUNCTION EXP(X) - 1 */
+/* ----------------------------------------------------------------------- */
+
+        double p1 = 9.14041914819518e-10;
+        double p2 = .0238082361044469;
+        double q1 = -.499999999085958;
+        double q2 = .107141568980644;
+        double q3 = -.0119041179760821;
+        double q4 = 5.95130811860248e-4;
+
+        if (fabs(x) <= 0.15) {
+            return x * (((p2 * x + p1) * x + 1.0) / ((((q4 * x + q3) * x + q2) * x + q1) * x + 1.0));
+        } else { /* |x| > 0.15 : */
+            double w = exp(x);
+            if (x > 0.0) {
+                return w * (0.5 - 1.0 / w + 0.5);
+            } else {
+                return w - 0.5 - 0.5;
+            }
+        }
+
+    } /* rexpm1 */
+
+    static double alnrel(double a) {
+/*
+ * ----------------------------------------------------------------------- Evaluation of the
+ * function ln(1 + a) -----------------------------------------------------------------------
+ */
+
+        if (fabs(a) > 0.375) {
+            return log(1. + a);
+        }
+        // else : |a| <= 0.375
+        double p1 = -1.29418923021993;
+        double p2 = .405303492862024;
+        double p3 = -.0178874546012214;
+        double q1 = -1.62752256355323;
+        double q2 = .747811014037616;
+        double q3 = -.0845104217945565;
+        double t = a / (a + 2.0);
+        double t2 = t * t;
+        double w = (((p3 * t2 + p2) * t2 + p1) * t2 + 1.) / (((q3 * t2 + q2) * t2 + q1) * t2 + 1.);
+        return t * 2.0 * w;
+
+    } /* alnrel */
+
+    static double rlog1(double x) {
+/*
+ * ----------------------------------------------------------------------- Evaluation of the
+ * function x - ln(1 + x) -----------------------------------------------------------------------
+ */
+
+        double a = .0566749439387324;
+        double b = .0456512608815524;
+        double p0 = .333333333333333;
+        double p1 = -.224696413112536;
+        double p2 = .00620886815375787;
+        double q1 = -1.27408923933623;
+        double q2 = .354508718369557;
+
+        if (x < -0.39 || x > 0.57) { /* direct evaluation */
+            double w = x + 0.5 + 0.5;
+            return x - log(w);
+        }
+        double h;
+        double w1;
+        /* else */
+        if (x < -0.18) { /* L10: */
+            h = x + .3;
+            h /= .7;
+            w1 = a - h * .3;
+        } else if (x > 0.18) { /* L20: */
+            h = x * .75 - .25;
+            w1 = b + h / 3.0;
+        } else { /* Argument Reduction */
+            h = x;
+            w1 = 0.0;
+        }
+
+/* L30: Series Expansion */
+
+        double r = h / (h + 2.0);
+        double t = r * r;
+        double w = ((p2 * t + p1) * t + p0) / ((q2 * t + q1) * t + 1.0);
+        return t * 2.0 * (1.0 / (1.0 - r) - r * w) + w1;
+
+    } /* rlog1 */
+
+    static double erf(double x) {
+/*
+ * ----------------------------------------------------------------------- EVALUATION OF THE REAL
+ * ERROR FUNCTION -----------------------------------------------------------------------
+ */
+
+        /* Initialized data */
+
+        double c = .564189583547756;
+        double[] a = {7.7105849500132e-5, -.00133733772997339, .0323076579225834, .0479137145607681, .128379167095513};
+        double[] b = {.00301048631703895, .0538971687740286, .375795757275549};
+        double[] p = {-1.36864857382717e-7, .564195517478974, 7.21175825088309, 43.1622272220567, 152.98928504694, 339.320816734344, 451.918953711873, 300.459261020162};
+        double[] q = {1., 12.7827273196294, 77.0001529352295, 277.585444743988, 638.980264465631, 931.35409485061, 790.950925327898, 300.459260956983};
+        double[] r = {2.10144126479064, 26.2370141675169, 21.3688200555087, 4.6580782871847, .282094791773523};
+        double[] s = {94.153775055546, 187.11481179959, 99.0191814623914, 18.0124575948747};
+
+        /* System generated locals */
+
+        double ax = fabs(x);
+        if (ax <= 0.5) {
+            double t = x * x;
+            double top = (((a[0] * t + a[1]) * t + a[2]) * t + a[3]) * t + a[4] + 1.0;
+            double bot = ((b[0] * t + b[1]) * t + b[2]) * t + 1.0;
+
+            return x * (top / bot);
+        }
+        /* else: ax > 0.5 */
+
+        if (ax <= 4.) { /* ax in (0.5, 4] */
+            double top = ((((((p[0] * ax + p[1]) * ax + p[2]) * ax + p[3]) * ax + p[4]) * ax + p[5]) * ax + p[6]) * ax + p[7];
+            double bot = ((((((q[0] * ax + q[1]) * ax + q[2]) * ax + q[3]) * ax + q[4]) * ax + q[5]) * ax + q[6]) * ax + q[7];
+            double retVal = 0.5 - exp(-x * x) * top / bot + 0.5;
+            if (x < 0.0) {
+                retVal = -retVal;
+            }
+            return retVal;
+        }
+
+        /* else: ax > 4 */
+
+        if (ax >= 5.8) {
+            return x > 0 ? 1 : -1;
+        }
+        double x2 = x * x;
+        double t = 1.0 / x2;
+        double top = (((r[0] * t + r[1]) * t + r[2]) * t + r[3]) * t + r[4];
+        double bot = (((s[0] * t + s[1]) * t + s[2]) * t + s[3]) * t + 1.0;
+        t = (c - top / (x2 * bot)) / ax;
+        double retVal = 0.5 - exp(-x2) * t + 0.5;
+        if (x < 0.0) {
+            retVal = -retVal;
+        }
+        return retVal;
+
+    } /* erf */
+
+    static double erfc1(int ind, double x) {
+/* ----------------------------------------------------------------------- */
+/* EVALUATION OF THE COMPLEMENTARY ERROR FUNCTION */
+
+/* ERFC1(IND,X) = ERFC(X) IF IND = 0 */
+/* ERFC1(IND,X) = EXP(X*X)*ERFC(X) OTHERWISE */
+/* ----------------------------------------------------------------------- */
+
+        /* Initialized data */
+
+        double c = .564189583547756;
+        double[] a = {7.7105849500132e-5, -.00133733772997339, .0323076579225834, .0479137145607681, .128379167095513};
+        double[] b = {.00301048631703895, .0538971687740286, .375795757275549};
+        double[] p = {-1.36864857382717e-7, .564195517478974, 7.21175825088309, 43.1622272220567, 152.98928504694, 339.320816734344, 451.918953711873, 300.459261020162};
+        double[] q = {1., 12.7827273196294, 77.0001529352295, 277.585444743988, 638.980264465631, 931.35409485061, 790.950925327898, 300.459260956983};
+        double[] r = {2.10144126479064, 26.2370141675169, 21.3688200555087, 4.6580782871847, .282094791773523};
+        double[] s = {94.153775055546, 187.11481179959, 99.0191814623914, 18.0124575948747};
+
+        double ax = fabs(x);
+        // |X| <= 0.5 */
+        if (ax <= 0.5) {
+            double t = x * x;
+            double top = (((a[0] * t + a[1]) * t + a[2]) * t + a[3]) * t + a[4] + 1.0;
+            double bot = ((b[0] * t + b[1]) * t + b[2]) * t + 1.0;
+            double retVal = 0.5 - x * (top / bot) + 0.5;
+            if (ind != 0) {
+                retVal = exp(t) * retVal;
+            }
+            return retVal;
+        }
+        double retVal;
+        // else (L10:): 0.5 < |X| <= 4
+        if (ax <= 4.0) {
+            double top = ((((((p[0] * ax + p[1]) * ax + p[2]) * ax + p[3]) * ax + p[4]) * ax + p[5]) * ax + p[6]) * ax + p[7];
+            double bot = ((((((q[0] * ax + q[1]) * ax + q[2]) * ax + q[3]) * ax + q[4]) * ax + q[5]) * ax + q[6]) * ax + q[7];
+            retVal = top / bot;
+        } else { // |X| > 4
+            // L20:
+            if (x <= -5.6) {
+                // L50: LIMIT VALUE FOR "LARGE" NEGATIVE X
+                retVal = 2.0;
+                if (ind != 0) {
+                    retVal = exp(x * x) * 2.0;
+                }
+                return retVal;
+            }
+            if (ind == 0 && (x > 100.0 || x * x > -exparg(1))) {
+                // LIMIT VALUE FOR LARGE POSITIVE X WHEN IND = 0
+                // L60:
+                return 0.0;
+            }
+
+            // L30:
+            double t = 1. / (x * x);
+            double top = (((r[0] * t + r[1]) * t + r[2]) * t + r[3]) * t + r[4];
+            double bot = (((s[0] * t + s[1]) * t + s[2]) * t + s[3]) * t + 1.0;
+            retVal = (c - t * top / bot) / ax;
+        }
+
+        // L40: FINAL ASSEMBLY
+        if (ind != 0) {
+            if (x < 0.0) {
+                retVal = exp(x * x) * 2.0 - retVal;
+            }
+        } else {
+            // L41: ind == 0 :
+            double w = x * x;
+            double t = w;
+            double e = w - t;
+            retVal = (0.5 - e + 0.5) * exp(-t) * retVal;
+            if (x < 0.0) {
+                retVal = 2.0 - retVal;
+            }
+        }
+        return retVal;
+
+    } /* erfc1 */
+
+    static double gam1(double a) {
+/* ------------------------------------------------------------------ */
+/* COMPUTATION OF 1/GAMMA(A+1) - 1 FOR -0.5 <= A <= 1.5 */
+/* ------------------------------------------------------------------ */
+
+        double t = a;
+        double d = a - 0.5;
+        // t := if(a > 1/2) a-1 else a
+        if (d > 0.0) {
+            t = d - 0.5;
+        }
+        if (t < 0.0) { /* L30: */
+            double[] r = {-.422784335098468, -.771330383816272, -.244757765222226, .118378989872749, 9.30357293360349e-4, -.0118290993445146, .00223047661158249, 2.66505979058923e-4,
+                            -1.32674909766242e-4};
+            double s1 = .273076135303957;
+            double s2 = .0559398236957378;
+
+            double top = (((((((r[8] * t + r[7]) * t + r[6]) * t + r[5]) * t + r[4]) * t + r[3]) * t + r[2]) * t + r[1]) * t + r[0];
+            double bot = (s2 * t + s1) * t + 1.0;
+            double w = top / bot;
+            debugPrintf("  gam1(a = %.15f): t < 0: w=%.15f\n", a, w);
+            if (d > 0.0) {
+                return t * w / a;
+            } else {
+                return a * (w + 0.5 + 0.5);
+            }
+        } else if (t == 0) { // L10: a in {0, 1}
+            return 0.;
+
+        } else { /* t > 0; L20: */
+            double[] p = {.577215664901533, -.409078193005776, -.230975380857675, .0597275330452234, .0076696818164949, -.00514889771323592, 5.89597428611429e-4};
+            double[] q = {1., .427569613095214, .158451672430138, .0261132021441447, .00423244297896961};
+
+            double top = (((((p[6] * t + p[5]) * t + p[4]) * t + p[3]) * t + p[2]) * t + p[1]) * t + p[0];
+            double bot = (((q[4] * t + q[3]) * t + q[2]) * t + q[1]) * t + 1.0;
+            double w = top / bot;
+            debugPrintf("  gam1(a = %.15f): t > 0: (is a < 1.5 ?)  w=%.15f\n", a, w);
+            if (d > 0.0) { /* L21: */
+                return t / a * (w - 0.5 - 0.5);
+            } else {
+                return a * w;
+            }
+        }
+    } /* gam1 */
+
+    static double gamln1(double a) {
+/* ----------------------------------------------------------------------- */
+/* EVALUATION OF LN(GAMMA(1 + A)) FOR -0.2 <= A <= 1.25 */
+/* ----------------------------------------------------------------------- */
+
+        double w;
+        if (a < 0.6) {
+            double p0 = .577215664901533;
+            double p1 = .844203922187225;
+            double p2 = -.168860593646662;
+            double p3 = -.780427615533591;
+            double p4 = -.402055799310489;
+            double p5 = -.0673562214325671;
+            double p6 = -.00271935708322958;
+            double q1 = 2.88743195473681;
+            double q2 = 3.12755088914843;
+            double q3 = 1.56875193295039;
+            double q4 = .361951990101499;
+            double q5 = .0325038868253937;
+            double q6 = 6.67465618796164e-4;
+            w = ((((((p6 * a + p5) * a + p4) * a + p3) * a + p2) * a + p1) * a + p0) / ((((((q6 * a + q5) * a + q4) * a + q3) * a + q2) * a + q1) * a + 1.);
+            return -(a) * w;
+        } else { /* 0.6 <= a <= 1.25 */
+            double r0 = .422784335098467;
+            double r1 = .848044614534529;
+            double r2 = .565221050691933;
+            double r3 = .156513060486551;
+            double r4 = .017050248402265;
+            double r5 = 4.97958207639485e-4;
+            double s1 = 1.24313399877507;
+            double s2 = .548042109832463;
+            double s3 = .10155218743983;
+            double s4 = .00713309612391;
+            double s5 = 1.16165475989616e-4;
+            double x = a - 0.5 - 0.5;
+            w = (((((r5 * x + r4) * x + r3) * x + r2) * x + r1) * x + r0) / (((((s5 * x + s4) * x + s3) * x + s2) * x + s1) * x + 1.0);
+            return x * w;
+        }
+    } /* gamln1 */
+
+    static double psi(double initialX) {
+        double x = initialX;
+/*
+ * ---------------------------------------------------------------------
+ * 
+ * Evaluation of the Digamma function psi(x)
+ * 
+ * -----------
+ * 
+ * Psi(xx) is assigned the value 0 when the digamma function cannot be computed.
+ * 
+ * The main computation involves evaluation of rational Chebyshev approximations published in Math.
+ * Comp. 27, 123-127(1973) by Cody, Strecok and Thacher.
+ */
+
+/* --------------------------------------------------------------------- */
+/* Psi was written at Argonne National Laboratory for the FUNPACK */
+/* package of special function subroutines. Psi was modified by */
+/* A.H. Morris (NSWC). */
+/* --------------------------------------------------------------------- */
+
+        double piov4 = .785398163397448; /* == pi / 4 */
+/* dx0 = zero of psi() to extended precision : */
+        double dx0 = 1.461632144968362341262659542325721325;
+
+/* --------------------------------------------------------------------- */
+/* COEFFICIENTS FOR RATIONAL APPROXIMATION OF */
+/* PSI(X) / (X - X0), 0.5 <= X <= 3.0 */
+        double[] p1 = {.0089538502298197, 4.77762828042627, 142.441585084029, 1186.45200713425, 3633.51846806499, 4138.10161269013, 1305.60269827897};
+        double[] q1 = {44.8452573429826, 520.752771467162, 2210.0079924783, 3641.27349079381, 1908.310765963, 6.91091682714533e-6};
+/* --------------------------------------------------------------------- */
+
+/* --------------------------------------------------------------------- */
+/* COEFFICIENTS FOR RATIONAL APPROXIMATION OF */
+/* PSI(X) - LN(X) + 1 / (2*X), X > 3.0 */
+
+        double[] p2 = {-2.12940445131011, -7.01677227766759, -4.48616543918019, -.648157123766197};
+        double[] q2 = {32.2703493791143, 89.2920700481861, 54.6117738103215, 7.77788548522962};
+/* --------------------------------------------------------------------- */
+
+/* MACHINE DEPENDENT CONSTANTS ... */
+
+/* --------------------------------------------------------------------- */
+/*
+ * XMAX1 = THE SMALLEST POSITIVE FLOATING POINT CONSTANT WITH ENTIRELY INT REPRESENTATION. ALSO USED
+ * AS NEGATIVE OF LOWER BOUND ON ACCEPTABLE NEGATIVE ARGUMENTS AND AS THE POSITIVE ARGUMENT BEYOND
+ * WHICH PSI MAY BE REPRESENTED AS LOG(X). Originally: xmax1 = amin1(ipmpar(3), 1./spmpar(1))
+ */
+        double xmax1 = INT_MAX;
+        double d2 = 0.5 / MathInit.d1mach(3); /* = 0.5 / (0.5 * DBL_EPS) = 1/DBL_EPSILON = 2^52 */
+        if (xmax1 > d2) {
+            xmax1 = d2;
+        }
+
+/* --------------------------------------------------------------------- */
+/* XSMALL = ABSOLUTE ARGUMENT BELOW WHICH PI*COTAN(PI*X) */
+/* MAY BE REPRESENTED BY 1/X. */
+        double xsmall = 1e-9;
+/* --------------------------------------------------------------------- */
+        double aug = 0.0;
+        if (x < 0.5) {
+/* --------------------------------------------------------------------- */
+/* X < 0.5, USE REFLECTION FORMULA */
+/* PSI(1-X) = PSI(X) + PI * COTAN(PI*X) */
+/* --------------------------------------------------------------------- */
+            if (fabs(x) <= xsmall) {
+
+                if (x == 0.0) {
+                    // goto L_err;
+                    return 0.;
+                }
+/* --------------------------------------------------------------------- */
+/* 0 < |X| <= XSMALL. USE 1/X AS A SUBSTITUTE */
+/* FOR PI*COTAN(PI*X) */
+/* --------------------------------------------------------------------- */
+                aug = -1.0 / x;
+            } else { /* |x| > xsmall */
+/* --------------------------------------------------------------------- */
+/* REDUCTION OF ARGUMENT FOR COTAN */
+/* --------------------------------------------------------------------- */
+                /* L100: */
+                double w = -x;
+                double sgn = piov4;
+                if (w <= 0.0) {
+                    w = -w;
+                    sgn = -sgn;
+                }
+/* --------------------------------------------------------------------- */
+/* MAKE AN ERROR EXIT IF |X| >= XMAX1 */
+/* --------------------------------------------------------------------- */
+                if (w >= xmax1) {
+                    // goto L_err;
+                    return 0.;
+                }
+                int nq = (int) w;
+                w -= nq;
+                nq = (int) (w * 4.0);
+                w = (w - nq * 0.25) * 4.0;
+/* --------------------------------------------------------------------- */
+/* W IS NOW RELATED TO THE FRACTIONAL PART OF 4.0 * X. */
+/* ADJUST ARGUMENT TO CORRESPOND TO VALUES IN FIRST */
+/* QUADRANT AND DETERMINE SIGN */
+/* --------------------------------------------------------------------- */
+                int n = nq / 2;
+                if (n + n != nq) {
+                    w = 1.0 - w;
+                }
+                double z = piov4 * w;
+                int m = n / 2;
+                if (m + m != n) {
+                    sgn = -sgn;
+                }
+/* --------------------------------------------------------------------- */
+/* DETERMINE FINAL VALUE FOR -PI*COTAN(PI*X) */
+/* --------------------------------------------------------------------- */
+                n = (nq + 1) / 2;
+                m = n / 2;
+                m += m;
+                if (m == n) {
+/* --------------------------------------------------------------------- */
+/* CHECK FOR SINGULARITY */
+/* --------------------------------------------------------------------- */
+                    if (z == 0.0) {
+                        // goto L_err;
+                        return 0.;
+                    }
+/* --------------------------------------------------------------------- */
+/* USE COS/SIN AS A SUBSTITUTE FOR COTAN, AND */
+/* SIN/COS AS A SUBSTITUTE FOR TAN */
+/* --------------------------------------------------------------------- */
+                    aug = sgn * (cos(z) / sin(z) * 4.0);
+
+                } else { /* L140: */
+                    aug = sgn * (sin(z) / cos(z) * 4.0);
+                }
+            }
+
+            x = 1.0 - x;
+
+        }
+        /* L200: */
+        if (x <= 3.0) {
+/* --------------------------------------------------------------------- */
+/* 0.5 <= X <= 3.0 */
+/* --------------------------------------------------------------------- */
+            double den = x;
+            double upper = p1[0] * x;
+
+            for (int i = 1; i <= 5; ++i) {
+                den = (den + q1[i - 1]) * x;
+                upper = (upper + p1[i]) * x;
+            }
+
+            den = (upper + p1[6]) / (den + q1[5]);
+            double xmx0 = x - dx0;
+            return den * xmx0 + aug;
+        }
+
+/* --------------------------------------------------------------------- */
+/* IF X >= XMAX1, PSI = LN(X) */
+/* --------------------------------------------------------------------- */
+        if (x < xmax1) {
+/* --------------------------------------------------------------------- */
+/* 3.0 < X < XMAX1 */
+/* --------------------------------------------------------------------- */
+            double w = 1.0 / (x * x);
+            double den = w;
+            double upper = p2[0] * w;
+
+            for (int i = 1; i <= 3; ++i) {
+                den = (den + q2[i - 1]) * w;
+                upper = (upper + p2[i]) * w;
+            }
+
+            aug = upper / (den + q2[3]) - 0.5 / x + aug;
+        }
+        return aug + log(x);
+
+/* --------------------------------------------------------------------- */
+/* ERROR RETURN */
+/* --------------------------------------------------------------------- */
+// L_err:
+        // return 0.;
+    } /* psi */
+
+    static double betaln(double a0, double b0) {
+/*
+ * ----------------------------------------------------------------------- Evaluation of the
+ * logarithm of the beta function ln(beta(a0,b0))
+ * -----------------------------------------------------------------------
+ */
+
+        double e = .918938533204673; // e == 0.5*LN(2*PI)
+
+        double a = min(a0, b0);
+        double b = max(a0, b0);
+
+        if (a < 8.0) {
+            if (a < 1.0) {
+/* ----------------------------------------------------------------------- */
+// A < 1
+/* ----------------------------------------------------------------------- */
+                if (b < 8.0) {
+                    return gamln(a) + (gamln(b) - gamln(a + b));
+                } else {
+                    return gamln(a) + algdiv(a, b);
+                }
+            }
+            /* else */
+/* ----------------------------------------------------------------------- */
+// 1 <= A < 8
+/* ----------------------------------------------------------------------- */
+            double w = 0.0;
+            boolean doL40 = false;
+            if (a < 2.0) {
+                if (b <= 2.0) {
+                    return gamln(a) + gamln(b) - gsumln(a, b);
+                }
+                /* else */
+
+                w = 0.0;
+                if (b < 8.0) {
+                    doL40 = true;
+                } else {
+                    return gamln(a) + algdiv(a, b);
+                }
+            }
+            // else L30: REDUCTION OF A WHEN B <= 1000
+
+            if (doL40 || b <= 1e3) {
+                int n;
+                if (!doL40) {
+                    n = (int) (a - 1.0);
+                    w = 1.0;
+                    for (int i = 1; i <= n; ++i) {
+                        a += -1.0;
+                        double h = a / b;
+                        w *= h / (h + 1.0);
+                    }
+                    w = log(w);
+
+                    if (b >= 8.0) {
+                        return w + gamln(a) + algdiv(a, b);
+                    }
+                }
+                // else
+// L40:
+                // reduction of B when B < 8
+                n = (int) (b - 1.0);
+                double z = 1.0;
+                for (int i = 1; i <= n; ++i) {
+                    b += -1.0;
+                    z *= b / (a + b);
+                }
+                return w + log(z) + (gamln(a) + (gamln(b) - gsumln(a, b)));
+            } else { // L50: reduction of A when B > 1000
+                int n = (int) (a - 1.0);
+                w = 1.0;
+                for (int i = 1; i <= n; ++i) {
+                    a += -1.0;
+                    w *= a / (a / b + 1.0);
+                }
+                return log(w) - n * log(b) + (gamln(a) + algdiv(a, b));
+            }
+
+        } else {
+/* ----------------------------------------------------------------------- */
+            // L60: A >= 8
+/* ----------------------------------------------------------------------- */
+
+            double w = bcorr(a, b);
+            double h = a / b;
+            double u = -(a - 0.5) * log(h / (h + 1.0));
+            double v = b * alnrel(h);
+            if (u > v) {
+                return log(b) * -0.5 + e + w - v - u;
+            } else {
+                return log(b) * -0.5 + e + w - u - v;
+            }
+        }
+
+    } /* betaln */
+
+    static double gsumln(double a, double b) {
+/* ----------------------------------------------------------------------- */
+/* EVALUATION OF THE FUNCTION LN(GAMMA(A + B)) */
+/* FOR 1 <= A <= 2 AND 1 <= B <= 2 */
+/* ----------------------------------------------------------------------- */
+
+        double x = a + b - 2.; // in [0, 2]
+
+        if (x <= 0.25) {
+            return gamln1(x + 1.0);
+        }
+
+        /* else */
+        if (x <= 1.25) {
+            return gamln1(x) + alnrel(x);
+        }
+        /* else x > 1.25 : */
+        return gamln1(x - 1.0) + log(x * (x + 1.0));
+    } /* gsumln */
+
+    static double bcorr(double a0, double b0) {
+/* ----------------------------------------------------------------------- */
+
+/* EVALUATION OF DEL(A0) + DEL(B0) - DEL(A0 + B0) WHERE */
+/* LN(GAMMA(A)) = (A - 0.5)*LN(A) - A + 0.5*LN(2*PI) + DEL(A). */
+/* IT IS ASSUMED THAT A0 >= 8 AND B0 >= 8. */
+
+/* ----------------------------------------------------------------------- */
+        /* Initialized data */
+
+        double c0 = .0833333333333333;
+        double c1 = -.00277777777760991;
+        double c2 = 7.9365066682539e-4;
+        double c3 = -5.9520293135187e-4;
+        double c4 = 8.37308034031215e-4;
+        double c5 = -.00165322962780713;
+
+/* ------------------------ */
+        double a = min(a0, b0);
+        double b = max(a0, b0);
+
+        double h = a / b;
+        double c = h / (h + 1.0);
+        double x = 1.0 / (h + 1.0);
+        double x2 = x * x;
+
+/* SET SN = (1 - X^N)/(1 - X) */
+
+        double s3 = x + x2 + 1.0;
+        double s5 = x + x2 * s3 + 1.0;
+        double s7 = x + x2 * s5 + 1.0;
+        double s9 = x + x2 * s7 + 1.0;
+        double s11 = x + x2 * s9 + 1.0;
+
+/* SET W = DEL(B) - DEL(A + B) */
+
+/* Computing 2nd power */
+        double r1 = 1.0 / b;
+        double t = r1 * r1;
+        double w = ((((c5 * s11 * t + c4 * s9) * t + c3 * s7) * t + c2 * s5) * t + c1 * s3) * t + c0;
+        w *= c / b;
+
+/* COMPUTE DEL(A) + W */
+
+/* Computing 2nd power */
+        r1 = 1.0 / a;
+        t = r1 * r1;
+        return (((((c5 * t + c4) * t + c3) * t + c2) * t + c1) * t + c0) / a + w;
+    } /* bcorr */
+
+    static double algdiv(double a, double b) {
+/* ----------------------------------------------------------------------- */
+
+/* COMPUTATION OF LN(GAMMA(B)/GAMMA(A+B)) WHEN B >= 8 */
+
+/* -------- */
+
+/* IN THIS ALGORITHM, DEL(X) IS THE FUNCTION DEFINED BY */
+/* LN(GAMMA(X)) = (X - 0.5)*LN(X) - X + 0.5*LN(2*PI) + DEL(X). */
+
+/* ----------------------------------------------------------------------- */
+
+        /* Initialized data */
+
+        double c0 = .0833333333333333;
+        double c1 = -.00277777777760991;
+        double c2 = 7.9365066682539e-4;
+        double c3 = -5.9520293135187e-4;
+        double c4 = 8.37308034031215e-4;
+        double c5 = -.00165322962780713;
+
+        double h;
+        double c;
+        double x;
+        double d;
+
+/* ------------------------ */
+        if (a > b) {
+            h = b / a;
+            c = 1.0 / (h + 1.0);
+            x = h / (h + 1.0);
+            d = a + (b - 0.5);
+        } else {
+            h = a / b;
+            c = h / (h + 1.0);
+            x = 1.0 / (h + 1.0);
+            d = b + (a - 0.5);
+        }
+
+/* Set s<n> = (1 - x^n)/(1 - x) : */
+
+        double x2 = x * x;
+        double s3 = x + x2 + 1.0;
+        double s5 = x + x2 * s3 + 1.0;
+        double s7 = x + x2 * s5 + 1.0;
+        double s9 = x + x2 * s7 + 1.0;
+        double s11 = x + x2 * s9 + 1.0;
+
+/* w := Del(b) - Del(a + b) */
+
+        double t = 1. / (b * b);
+        double w = ((((c5 * s11 * t + c4 * s9) * t + c3 * s7) * t + c2 * s5) * t + c1 * s3) * t + c0;
+        w *= c / b;
+
+/* COMBINE THE RESULTS */
+
+        double u = d * alnrel(a / b);
+        double v = a * (log(b) - 1.0);
+        if (u > v) {
+            return w - v - u;
+        } else {
+            return w - u - v;
+        }
+    } /* algdiv */
+
+    static double gamln(double a) {
+/*
+ * ----------------------------------------------------------------------- Evaluation of
+ * ln(gamma(a)) for positive a
+ * -----------------------------------------------------------------------
+ */
+/* Written by Alfred H. Morris */
+/* Naval Surface Warfare Center */
+/* Dahlgren, Virginia */
+/* ----------------------------------------------------------------------- */
+
+        double d = .418938533204673; // d == 0.5*(LN(2*PI) - 1)
+
+        double c0 = .0833333333333333;
+        double c1 = -.00277777777760991;
+        double c2 = 7.9365066682539e-4;
+        double c3 = -5.9520293135187e-4;
+        double c4 = 8.37308034031215e-4;
+        double c5 = -.00165322962780713;
+
+        if (a <= 0.8) {
+            return gamln1(a) - log(a); /* ln(G(a+1)) - ln(a) == ln(G(a+1)/a) = ln(G(a)) */
+        } else if (a <= 2.25) {
+            return gamln1(a - 0.5 - 0.5);
+        } else if (a < 10.0) {
+            int n = (int) (a - 1.25);
+            double t = a;
+            double w = 1.0;
+            for (int i = 1; i <= n; ++i) {
+                t += -1.0;
+                w *= t;
+            }
+            return gamln1(t - 1.) + log(w);
+        } else { /* a >= 10 */
+            double t = 1. / (a * a);
+            double w = (((((c5 * t + c4) * t + c3) * t + c2) * t + c1) * t + c0) / a;
+            return d + w + (a - 0.5) * (log(a) - 1.0);
+        }
+    } /* gamln */
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
index 3982150391..a8fa0cb6fe 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
@@ -493,6 +493,8 @@ public class ForeignFunctions {
                     return RunifNodeGen.create();
                 case "qgamma":
                     return QgammaNodeGen.create();
+                case "qbinom":
+                    return QbinomNodeGen.create();
                 case "download":
                     return new Download();
                 case "unzip":
diff --git a/mx.fastr/copyrights/gnu_r.core.copyright.star b/mx.fastr/copyrights/gnu_r.core.copyright.star
new file mode 100644
index 0000000000..c05aa12b5e
--- /dev/null
+++ b/mx.fastr/copyrights/gnu_r.core.copyright.star
@@ -0,0 +1,10 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 1995-2015, The R Core Team
+ * Copyright (c) 2015, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
diff --git a/mx.fastr/copyrights/gnu_r.core.copyright.star.regex b/mx.fastr/copyrights/gnu_r.core.copyright.star.regex
new file mode 100644
index 0000000000..e625a25f30
--- /dev/null
+++ b/mx.fastr/copyrights/gnu_r.core.copyright.star.regex
@@ -0,0 +1 @@
+/\*\n \* This material is distributed under the GNU General Public License\n \* Version 2. You may review the terms of this license at\n \* http://www.gnu.org/licenses/gpl-2.0.html\n \*\n \* Copyright \(c\) (?:[1-2][09][0-9][0-9]--)?[1-2][09][0-9][0-9], The R Core Team\n \* Copyright \(c\) (?:(20[0-9][0-9]), )?(20[0-9][0-9]), Oracle and/or its affiliates\n \*\n \* All rights reserved.\n \*/\n.*
\ No newline at end of file
diff --git a/mx.fastr/copyrights/gnu_r_ihaka.copyright.star.regex b/mx.fastr/copyrights/gnu_r_ihaka.copyright.star.regex
index b7d037f67c..f501f18d94 100644
--- a/mx.fastr/copyrights/gnu_r_ihaka.copyright.star.regex
+++ b/mx.fastr/copyrights/gnu_r_ihaka.copyright.star.regex
@@ -1 +1 @@
-/\*\n \* This material is distributed under the GNU General Public License\n \* Version 2. You may review the terms of this license at\n \* http://www.gnu.org/licenses/gpl-2.0.html\n \*\n \* Copyright \(C\) 1998 Ross Ihaka\n \* Copyright \(c\) 1998--2012, The R Core Team\n \* Copyright \(c\) 2004, The R Foundation\n \* Copyright \(c\) (?:(20[0-9][0-9]), )?(20[0-9][0-9]), Oracle and/or its affiliates\n \*\n \* All rights reserved.\n \*/\n.*
+/\*\n \* This material is distributed under the GNU General Public License\n \* Version 2. You may review the terms of this license at\n \* http://www.gnu.org/licenses/gpl-2.0.html\n \*\n \* Copyright \(C\) 1998 Ross Ihaka\n \* Copyright \(c\) (?:[1-2][09][0-9][0-9]--)?[1-2][09][0-9][0-9], The R Core Team\n \* Copyright \(c\) (?:[1-2][09][0-9][0-9]--)?[1-2][09][0-9][0-9], The R Foundation\n \* Copyright \(c\) (?:(20[0-9][0-9]), )?(20[0-9][0-9]), Oracle and/or its affiliates\n \*\n \* All rights reserved.\n \*/\n.*
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index e914bfe324..d3d5e3ad05 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -29,11 +29,18 @@ com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsLis
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/Slot.java,gnu_r.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/CompleteCases.java,gnu_r_gentleman_ihaka2.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java,gnu_r.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/DPQ.java,gnu_r.core.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/GammaFunctions.java,gnu_r_qgamma.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/MathConstants.java,gnu_r.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/MathInit.java,gnu_r.core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Pbeta.java,gnu_r.core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Pbinom.java,gnu_r_ihaka.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Qbinom.java,gnu_r_ihaka.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Random2.java,gnu_r.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Rnorm.java,gnu_r.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/SplineFunctions.java,gnu_r_splines.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/StatsUtil.java,gnu_r_ihaka.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/TOMS708.java,gnu_r.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/DirChmod.java,gnu_r.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsText.java,gnu_r.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/CountFields.java,gnu_r.copyright
-- 
GitLab