// @source_type: c
// @source_origin: pnt.c
// @includes: nmath.h, dpq.h
// @depends: df, expm1, fmin2, lgamma, pbeta, pnorm, pt, nmath, dpq
// @provides: pnt
// @all_depends_count: 33
// @all_depends: dpq, refactored, Rmath, nmath, stirlerr_cycle_free, chebyshev, cospi, d1mach, dnorm, fmax2, fmin2, gammalims, i1mach, lgammacor, log1p, pnorm, expm1, gamma, lgamma, pgamma_utils, stirlerr_cycle_dependent, bd0, lbeta, stirlerr, beta, dbinom, dpois, pgamma, toms708, dgamma, pbeta, pt, df
// @load_order: 122

/*
 *  Mathlib : A C Library of Special Functions
 *  Copyright (C) 1998-2015 The R Core Team
 *  based on AS243 (C) 1989 Royal Statistical Society
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, a copy is available at
 *  https://www.R-project.org/Licenses/
 */

/*  Algorithm AS 243  Lenth,R.V. (1989). Appl. Statist., Vol.38, 185-189.
 *  ----------------
 *  Cumulative probability at t of the non-central t-distribution
 *  with df degrees of freedom (may be fractional) and non-centrality
 *  parameter delta.
 *
 *  NOTE
 *
 *    Requires the following auxiliary routines:
 *
 *	lgammafn(x)	- log gamma function
 *	pbeta(x, a, b)	- incomplete beta function
 *	pnorm(x)	- normal distribution function
 *
 *  CONSTANTS
 *
 *    M_SQRT_2dPI  = 1/ {gamma(1.5) * sqrt(2)} = sqrt(2 / pi)
 *    M_LN_SQRT_PI = ln(sqrt(pi)) = ln(pi)/2
 */

// openclport: include directives disabled for OpenCL C compilation.
// openclport: preload equivalent ported headers/shims in program assembly.
// openclport-disabled-include: #include "nmath.h"
// openclport-disabled-include: #include "dpq.h"

/*----------- DEBUGGING -------------
 *
 *	make CFLAGS='-DDEBUG_pnt -g'

 * -- Feb.3, 1999; M.Maechler:
	- For 't > ncp > 20' (or so)	the result is completely WRONG!  <== no longer true
	- but for ncp > 100
 */

double pnt(double t, double df, double ncp, int lower_tail, int log_p)
{
    double albeta, a, b, del, errbd, lambda, rxb, tt, x;
    LDOUBLE geven, godd, p, q, s, tnc, xeven, xodd;
    int it, negdel;

    /* note - itrmax and errmax may be changed to suit one's needs. */

    const int itrmax = 1000;
    const static double errmax = 1.e-12;

    if (df <= 0.0) ML_WARN_return_NAN;
    if(ncp == 0.0) return pt(t, df, lower_tail, log_p);

    if(!R_FINITE(t))
	return (t < 0) ? R_DT_0 : R_DT_1;
    if (t >= 0.) {
	negdel = FALSE; tt = t;	 del = ncp;
    }
    else {
	/* We deal quickly with left tail if extreme,
	   since pt(q, df, ncp) <= pt(0, df, ncp) = \Phi(-ncp) */
	if (ncp > 40 && (!log_p || !lower_tail)) return R_DT_0;
	negdel = TRUE;	tt = -t; del = -ncp;
    }

    if (df > 4e5 || del*del > 2*M_LN2*(-(DBL_MIN_EXP))) {
	/*-- 2nd part: if del > 37.62, then p=0 below
	  FIXME: test should depend on `df', `tt' AND `del' ! */
	/* Approx. from	 Abramowitz & Stegun 26.7.10 (p.949) */
	s = 1./(4.*df);

	return pnorm((double)(tt*(1. - s)), del,
		     sqrt((double) (1. + tt*tt*2.*s)),
		     lower_tail != negdel, log_p);
    }

    /* initialize twin series */
    /* Guenther, J. (1978). Statist. Computn. Simuln. vol.6, 199. */

    x = t * t;
    rxb = df/(x + df);/* := (1 - x) {x below} -- but more accurately */
    x = x / (x + df);/* in [0,1) */
#ifdef DEBUG_pnt
    REprintf("pnt(t=%7g, df=%7g, ncp=%7g) ==> x= %10g:",t,df,ncp, x);
#endif
    if (x > 0.) {/* <==>  t != 0 */
	lambda = del * del;
	p = .5 * exp(-.5 * lambda);
#ifdef DEBUG_pnt
	REprintf("\t p=%10Lg\n",p);
#endif
	if(p == 0.) { /* underflow! */

	    /*========== really use an other algorithm for this case !!! */
	    ML_WARNING(ME_UNDERFLOW, "pnt");
	    ML_WARNING(ME_RANGE, "pnt"); /* |ncp| too large */
	    return R_DT_0;
	}
#ifdef DEBUG_pnt
	REprintf("it  1e5*(godd,   geven)|          p           q           s"
	       /* 1.3 1..4..7.9 1..4..7.9|1..4..7.901 1..4..7.901 1..4..7.901 */
		 "        pnt(*)     errbd\n");
	       /* 1..4..7..0..34 1..4..7.9*/
#endif
	q = M_SQRT_2dPI * p * del;
	s = .5 - p;
	/* s = 0.5 - p = 0.5*(1 - exp(-.5 L)) =  -0.5*expm1(-.5 L)) */
	if(s < 1e-7)
	    s = -0.5 * expm1(-0.5 * lambda);
	a = .5;
	b = .5 * df;
	/* rxb = (1 - x) ^ b   [ ~= 1 - b*x for tiny x --> see 'xeven' below]
	 *       where '(1 - x)' =: rxb {accurately!} above */
	rxb = pow(rxb, b);
	albeta = M_LN_SQRT_PI + lgammafn(b) - lgammafn(.5 + b);
	xodd = pbeta(x, a, b, /*lower*/TRUE, /*log_p*/FALSE);
	godd = 2. * rxb * exp(a * log(x) - albeta);
	tnc = b * x;
	xeven = (tnc < DBL_EPSILON) ? tnc : 1. - rxb;
	geven = tnc * rxb;
	tnc = p * xodd + q * xeven;

	/* repeat until convergence or iteration limit */
	for(it = 1; it <= itrmax; it++) {
	    a += 1.;
	    xodd  -= godd;
	    xeven -= geven;
	    godd  *= x * (a + b - 1.) / a;
	    geven *= x * (a + b - .5) / (a + .5);
	    p *= lambda / (2 * it);
	    q *= lambda / (2 * it + 1);
	    tnc += p * xodd + q * xeven;
	    s -= p;
	    /* R 2.4.0 added test for rounding error here. */
	    if(s < -1.e-10) { /* happens e.g. for (t,df,ncp)=(40,10,38.5), after 799 it.*/
		ML_WARNING(ME_PRECISION, "pnt");
#ifdef DEBUG_pnt
		REprintf("s = %#14.7Lg < 0 !!! ---> non-convergence!!\n", s);
#endif
		goto finis;
	    }
	    if(s <= 0 && it > 1) goto finis;
	    errbd = (double)(2. * s * (xodd - godd));
#ifdef DEBUG_pnt
	    REprintf("%3d %#9.4g %#9.4g|%#11.4Lg %#11.4Lg %#11.4Lg %#14.10Lg %#9.4g\n",
		     it, 1e5*(double)godd, 1e5*(double)geven, p, q, s, tnc, errbd);
#endif
	    if(fabs(errbd) < errmax) goto finis;/*convergence*/
	}
	/* non-convergence:*/
	ML_WARNING(ME_NOCONV, "pnt");
    }
    else { /* x = t = 0 */
	tnc = 0.;
    }
 finis:
    tnc += pnorm(- del, 0., 1., /*lower*/TRUE, /*log_p*/FALSE);

    lower_tail = lower_tail != negdel; /* xor */
    if(tnc > 1 - 1e-10 && lower_tail)
	ML_WARNING(ME_PRECISION, "pnt{final}");

    return R_DT_val(fmin2((double)tnc, 1.) /* Precaution */);
}
