package DistLib;

/* data translated from C using perl script translate.pl */
/* script version 0.00                               */


public class poisson 
  { 
    /*
     *  DistLib : A C Library of Special Functions
     *  Copyright (C) 1998 Ross Ihaka
     *
     *  This program is free software; you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License as published by
     *  the Free Software Foundation; either version 2 of the License, or
     *  (at your option) any later version.
     *
     *  This program is distributed in the hope that it will be useful,
     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     *  GNU General Public License for more details.
     *
     *  You should have received a copy of the GNU General Public License
     *  along with this program; if not, write to the Free Software
     *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     *
     *  SYNOPSIS
     *
     *    #include "DistLib.h"
     *    double density(double x, double lambda)
     *
     *  DESCRIPTION
     *
     *    The density function of the Poisson distribution.
     */
    
    /*!* #include "DistLib.h" /*4!*/
    
    public static double  density(double x, double lambda)
    {
    /*!* #ifdef IEEE_754 /*4!*/
        if(Double.isNaN(x) || Double.isNaN(lambda))
    	return x + lambda;
    /*!* #endif /*4!*/
/*!*     x = floor(x + 0.5); *!*/
        x = java.lang.Math.floor(x + 0.5);
        if(lambda <= 0.0) {
    	throw new java.lang.ArithmeticException("Math Error: DOMAIN");
	//    	return Double.NaN;
        }
        if (x < 0)
    	return 0;
    /*!* #ifdef IEEE_754 /*4!*/
        if(Double.isInfinite(x))
    	return 0;
    /*!* #endif /*4!*/
/*!*     return exp(x * log(lambda) - lambda - lgammafn(x + 1)); *!*/
        return java.lang.Math.exp(x * java.lang.Math.log(lambda) - lambda - misc.lgammafn(x + 1));
    }
    /*
     *  DistLib : A C Library of Special Functions
     *  Copyright (C) 1998 Ross Ihaka
     *
     *  This program is free software; you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License as published by
     *  the Free Software Foundation; either version 2 of the License, or
     *  (at your option) any later version.
     *
     *  This program is distributed in the hope that it will be useful,
     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     *  GNU General Public License for more details.
     *
     *  You should have received a copy of the GNU General Public License
     *  along with this program; if not, write to the Free Software
     *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     *
     *  SYNOPSIS
     *
     *    #include "DistLib.h"
     *    double cumulative(double x, double lambda)
     *
     *  DESCRIPTION
     *
     *    The distribution function of the Poisson distribution.
     */
    
    /*!* #include "DistLib.h" /*4!*/
    
    public static double  cumulative(double x, double lambda)
    {
    /*!* #ifdef IEEE_754 /*4!*/
        if (Double.isNaN(x) || Double.isNaN(lambda))
    	return x + lambda;
    /*!* #endif /*4!*/
/*!*     x = floor(x + 0.5); *!*/
        x = java.lang.Math.floor(x + 0.5);
        if(lambda <= 0.0) {
    	throw new java.lang.ArithmeticException("Math Error: DOMAIN");
	//    	return Double.NaN;
        }
        if (x < 0)
    	return 0;
    /*!* #ifdef IEEE_754 /*4!*/
        if (Double.isInfinite(x))
    	return 1;
    /*!* #endif /*4!*/
        return  1 - gamma.cumulative(lambda, x + 1, 1.0);
    }
    /*
     *  DistLib : A C Library of Special Functions
     *  Copyright (C) 1998 Ross Ihaka
     *
     *  This program is free software; you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License as published by
     *  the Free Software Foundation; either version 2 of the License, or
     *  (at your option) any later version.
     *
     *  This program is distributed in the hope that it will be useful,
     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     *  GNU General Public License for more details.
     *
     *  You should have received a copy of the GNU General Public License
     *  along with this program; if not, write to the Free Software
     *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     *
     *  SYNOPSIS
     *
     *    #include "DistLib.h"
     *    double quantile(double x, double lambda)
     *
     *  DESCRIPTION
     *
     *    The quantile function of the Poisson distribution.
     *
     *  METHOD
     *
     *    Uses the Cornish-Fisher Expansion to include a skewness
     *    correction to a normal approximation.  This gives an
     *    initial value which never seems to be off by more than
     *    1 or 2.  A search is then conducted of values close to
     *    this initial start point.
     */
    
    /*!* #include "DistLib.h" /*4!*/
    
    public static double  quantile(double x, double lambda)
    {
        double mu, sigma, gamma, z, y;
    /*!* #ifdef IEEE_754 /*4!*/
        if (Double.isNaN(x) || Double.isNaN(lambda))
    	return x + lambda;
        if(Double.isInfinite(lambda)) {
    	throw new java.lang.ArithmeticException("Math Error: DOMAIN");
	//    	return Double.NaN;
        }
    /*!* #endif /*4!*/
        if(x < 0 || x > 1 || lambda <= 0) {
    	throw new java.lang.ArithmeticException("Math Error: DOMAIN");
	//    	return Double.NaN;
        }
        if (x == 0) return 0;
    /*!* #ifdef IEEE_754 /*4!*/
        if (x == 1) return Double.POSITIVE_INFINITY;
    /*!* #endif /*4!*/
        mu = lambda;
/*!*     sigma = sqrt(lambda); *!*/
        sigma = java.lang.Math.sqrt(lambda);
        gamma = sigma;
        z = normal.quantile(x, 0.0, 1.0);
/*!*     y = floor(mu + sigma * (z + gamma * (z * z - 1) / 6) + 0.5); *!*/
        y = java.lang.Math.floor(mu + sigma * (z + gamma * (z * z - 1) / 6) + 0.5);
        z = cumulative(y, lambda);
    
        if(z >= x) {
    
    	/* search to the left */
    
    	for(;;) {
    	    if((z = poisson.cumulative(y - 1, lambda)) < x)
    		return y;
    	    y = y - 1;
    	}
        }
        else {
    
    	/* search to the right */
    
    	for(;;) {
    	    if((z = poisson.cumulative(y + 1, lambda)) >= x)
    		return y + 1;
    	    y = y + 1;
    	}
        }
    }
    
    /*
     *  Mathlib : A C Library of Special Functions
     *  Copyright (C) 1998 Ross Ihaka
     *  Copyright (C) 2000-2011 The R Core Team
     *
     *  This program is free software; you can redistribute it and/or modify
     *  it under the terms of the GNU General Public License as published by
     *  the Free Software Foundation; either version 2 of the License, or
     *  (at your option) any later version.
     *
     *  This program is distributed in the hope that it will be useful,
     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     *  GNU General Public License for more details.
     *
     *  You should have received a copy of the GNU General Public License
     *  along with this program; if not, a copy is available at
     *  https://www.R-project.org/Licenses/
     *
     *  SYNOPSIS
     *
     *    #include <Rmath.h>
     *    double rpois(double lambda)
     *
     *  DESCRIPTION
     *
     *    Random variates from the Poisson distribution.
     *
     *  REFERENCE
     *
     *    Ahrens, J.H. and Dieter, U. (1982).
     *    Computer generation of Poisson deviates
     *    from modified normal distributions.
     *    ACM Trans. Math. Software 8, 163-179.
     */
    
    /* Factorial Table (0:9)! */
    static double fact[] =
    {
    	1., 1., 2., 6., 24., 120., 720., 5040., 40320., 362880.
    };
    
    
    static private double a0 = -0.5 ;
    static private double a1 = 0.3333333 ;
    static private double a2 = -0.2500068 ;
    static private double a3 = 0.2000118 ;
    static private double a4 = -0.1661269 ; 
    static private double a5 = 0.1421878 ;
    static private double a6 = -0.1384794 ;
    static private double a7 = 0.1250060 ;
    
    static private double one_7 = 0.1428571428571428571 ;
    static private double one_12 = 0.0833333333333333333 ;
    static private double one_24 = 0.0416666666666666667 ;
    
    public static double  random(double mu, uniform PRNG ) {
        int l=0, m=1;
    
        double b1, b2, c=1., c0=1., c1=1., c2=1., c3=1.;
        double pp[] = new double[36];
        double p0=Math.exp(-mu), p=Math.exp(-mu), q=Math.exp(-mu), s= Math.sqrt(mu), d=6.*mu*mu, omega=1.;
        double big_l=0.;/* integer "w/o overflow" */
        double muprev = 0., muprev2 = 0.;/*, muold	 = 0.*/
    
        /* Local Vars  [initialize some for -Wall]: */
        double del, difmuk= 0., E= 0., fk= 0., fx, fy, g, px, py, t=0., u= 0., v, x;
        double pois = -1.;
        int k, kflag=1;
        boolean big_mu;
        boolean new_big_mu = false;
    
        if (Double.isInfinite(mu) || mu < 0)
    	    throw new java.lang.ArithmeticException("Math Error: DOMAIN");
    
        if (mu <= 0.)
    	    return 0.;
    
        big_mu = mu >= 10.;
        if (big_mu)
    	    new_big_mu = false;
    
        if (!(big_mu && mu == muprev)) {/* maybe compute new persistent par.s */
            
    	    if (big_mu) {
    	        new_big_mu = true;
    	        /* Case A. (recalculation of s,d,l	because mu has changed):
    	         * The poisson probabilities pk exceed the discrete normal
    	         * probabilities fk whenever k >= m(mu).
    	         */
    	        muprev = mu;
    	        s = Math.sqrt(mu);
    	        d = 6. * mu * mu;
    	        big_l = Math.floor(mu - 1.1484);
    	        /* = an upper bound to m(mu) for all mu >= 10.*/
    	    }
    	    else { /* Small mu ( < 10) -- not using normal approx. */
                
    	        /* Case B. (start new table and calculate p0 if necessary) */
        
    	        /*muprev = 0.;-* such that next time, mu != muprev ..*/
    	        if (mu != muprev) {
    		    muprev = mu;
    		    m = Math.max(1, (int) mu);
    		    l = 0; /* pp[] is already ok up to pp[l] */
    		    q = p0 = p = Math.exp(-mu);
    	        }
                
    	        for (;;) {
    		    /* Step U. uniform sample for inversion method */
    		    u = uniform.random();
    		    if (u <= p0)
    		        return 0.;
                    
    		    /* Step T. table comparison until the end pp[l] of the
    		       pp-table of cumulative poisson probabilities
    		       (0.458 > ~= pp[9](= 0.45792971447) for mu=10 ) */
    		    if (l != 0) {
    		        for (k = (u <= 0.458) ? 1 : Math.min(l, m);  k <= l; k++)
    			    if (u <= pp[k])
    			        return (double)k;
    		        if (l == 35) /* u > pp[35] */
    			    continue;
    		    }
    		    /* Step C. creation of new poisson
    		       probabilities p[l..] and their cumulatives q =: pp[k] */
    		    l++;
    		    for (k = l; k <= 35; k++) {
    		        p *= mu / k;
    		        q += p;
    		        pp[k] = q;
    		        if (u <= q) {
    			    l = k;
    			    return (double)k;
    		        }
    		    }
    		    l = 35;
    	        } /* end(repeat) */
    	    }/* mu < 10 */
        
        } /* end {initialize persistent vars} */
        
        /* Only if mu >= 10 : ----------------------- */
        
        /* Step N. normal sample */
        g = mu + s * normal.random(PRNG);/* norm_rand() ~ N(0,1), standard normal */
    
        if (g >= 0.) {
    	    pois = Math.floor(g);
    	    /* Step I. immediate acceptance if pois is large enough */
    	    if (pois >= big_l)
    	        return pois;
    	    /* Step S. squeeze acceptance */
    	    fk = pois;
    	    difmuk = mu - fk;
    	    u = uniform.random(); /* ~ U(0,1) - sample */
    	    if (d * u >= difmuk * difmuk * difmuk)
    	        return pois;
            }
        
        /* Step P. preparations for steps Q and H.
           (recalculations of parameters if necessary) */
    
        if (new_big_mu || mu != muprev2) {
            /* Careful! muprev2 is not always == muprev
    	   because one might have exited in step I or S
    	   */
            muprev2 = mu;
    	    omega = 1./Math.sqrt(2.*Math.PI) / s;
    	    /* The quantities b1, b2, c3, c2, c1, c0 are for the Hermite
    	     * approximations to the discrete normal probabilities fk. */
            
    	    b1 = one_24 / mu;
    	    b2 = 0.3 * b1 * b1;
    	    c3 = one_7 * b1 * b2;
    	    c2 = b2 - 15. * c3;
    	    c1 = b1 - 6. * b2 + 45. * c3;
    	    c0 = 1. - b1 + 3. * b2 - 15. * c3;
    	    c = 0.1069 / mu; /* guarantees majorization by the 'hat'-function. */
        }
        
        boolean skipBeginningFor=false;
    
        if (g >= 0.) {
    	    /* 'Subroutine' F is called (kflag=0 for correct return) */
    	    kflag = 0;
    	    skipBeginningFor = true;
        }
        
        
        for(;;) {
            if (!skipBeginningFor) {
    	        /* Step E. Exponential Sample */
             
    	     E = exponential.random(PRNG);	/* ~ Exp(1) (standard exponential) */
                
    	        /*  sample t from the laplace 'hat'
    	            (if t <= -0.6744 then pk < fk for all mu >= 10.) */
    	        u = 2 * uniform.random() - 1.;
    	        t = 1.8 + misc.fsign(E, u);
            }
    	    if (t > -0.6744 || skipBeginningFor) {
                if (!skipBeginningFor) {
    	            pois = Math.floor(mu + s * t);
    	            fk = pois;
    	            difmuk = mu - fk;
                    
    	            /* 'subroutine' F is called (kflag=1 for correct return) */
    	            kflag = 1;
                    
                    skipBeginningFor=false;
                }
                
    	        /* 'subroutine' F : calculation of px,py,fx,fy. */
                
    	        if (pois < 10) { /* use factorials from table fact[] */
    		    px = -mu;
    		    py = Math.pow(mu, pois) / fact[(int)pois];
    	        }
    	        else {
    		    /* Case pois >= 10 uses polynomial approximation
    		       a0-a7 for accuracy when advisable */
    		    del = one_12 / fk;
    		    del = del * (1. - 4.8 * del * del);
    		    v = difmuk / fk;
    		    if (Math.abs(v) <= 0.25)
    		        px = fk * v * v * (((((((a7 * v + a6) * v + a5) * v + a4) *
    			         	  v + a3) * v + a2) * v + a1) * v + a0)
    			    - del;
    		    else /* |v| > 1/4 */
    		        px = fk * Math.log(1. + v) - difmuk - del;
    		    py = 1./Math.sqrt(2.*Math.PI) / Math.sqrt(fk);
    	        }
    	        x = (0.5 - difmuk) / s;
    	        x *= x;/* x^2 */
    	        fx = -0.5 * x;
    	        fy = omega * (((c3 * x + c2) * x + c1) * x + c0);
    	        if (kflag > 0) {
    		    /* Step H. Hat acceptance (E is repeated on rejection) */
    		    if (c * Math.abs(u) <= py * Math.exp(px + E) - fy * Math.exp(fx + E))
    		        break;
    	        } else {
    		    /* Step Q. Quotient acceptance (rare case) */
    		    if (fy - u * fy <= py * Math.exp(px - fx))
    		        break;
                }
    	    }/* t > -.67.. */
        }
        return pois;
    }
}
