summaryrefslogtreecommitdiff
path: root/numpy/random/mtrand/distributions.c
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/random/mtrand/distributions.c')
-rw-r--r--numpy/random/mtrand/distributions.c845
1 files changed, 845 insertions, 0 deletions
diff --git a/numpy/random/mtrand/distributions.c b/numpy/random/mtrand/distributions.c
new file mode 100644
index 000000000..3f5ff2355
--- /dev/null
+++ b/numpy/random/mtrand/distributions.c
@@ -0,0 +1,845 @@
+/* Copyright 2005 Robert Kern (robert.kern@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* The implementations of rk_hypergeometric_hyp(), rk_hypergeometric_hrua(),
+ * and rk_triangular() were adapted from Ivan Frohne's rv.py which has this
+ * license:
+ *
+ * Copyright 1998 by Ivan Frohne; Wasilla, Alaska, U.S.A.
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation for any purpose, free of charge, is granted subject to the
+ * following conditions:
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the software.
+ *
+ * THE SOFTWARE AND DOCUMENTATION IS PROVIDED WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR
+ * OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM OR DAMAGES IN A CONTRACT
+ * ACTION, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR ITS DOCUMENTATION.
+ */
+
+#include <math.h>
+#include "distributions.h"
+#include <stdio.h>
+
+#ifndef min
+#define min(x,y) ((x<y)?x:y)
+#define max(x,y) ((x>y)?x:y)
+#endif
+
+/* log-gamma function to support some of these distributions. The
+ * algorithm comes from SPECFUN by Shanjie Zhang and Jianming Jin and their
+ * book "Computation of Special Functions", 1996, John Wiley & Sons, Inc.
+ */
+extern double loggam(double x);
+double loggam(double x)
+{
+ double x0, x2, xp, gl, gl0;
+ long k, n;
+
+ static double a[10] = {8.333333333333333e-02,-2.777777777777778e-03,
+ 7.936507936507937e-04,-5.952380952380952e-04,
+ 8.417508417508418e-04,-1.917526917526918e-03,
+ 6.410256410256410e-03,-2.955065359477124e-02,
+ 1.796443723688307e-01,-1.39243221690590e+00};
+ x0 = x;
+ n = 0;
+ if ((x == 1.0) || (x == 2.0))
+ {
+ return 0.0;
+ }
+ else if (x <= 7.0)
+ {
+ n = (long)(7 - x);
+ x0 = x + n;
+ }
+ x2 = 1.0/(x0*x0);
+ xp = 2*M_PI;
+ gl0 = a[9];
+ for (k=8; k>=0; k--)
+ {
+ gl0 *= x2;
+ gl0 += a[k];
+ }
+ gl = gl0/x0 + 0.5*log(xp) + (x0-0.5)*log(x0) - x0;
+ if (x <= 7.0)
+ {
+ for (k=1; k<=n; k++)
+ {
+ gl -= log(x0-1.0);
+ x0 -= 1.0;
+ }
+ }
+ return gl;
+}
+
+double rk_normal(rk_state *state, double loc, double scale)
+{
+ return loc + scale*rk_gauss(state);
+}
+
+double rk_standard_exponential(rk_state *state)
+{
+ /* We use -log(1-U) since U is [0, 1) */
+ return -log(1.0 - rk_double(state));
+}
+
+double rk_exponential(rk_state *state, double scale)
+{
+ return scale * rk_standard_exponential(state);
+}
+
+double rk_uniform(rk_state *state, double loc, double scale)
+{
+ return loc + scale*rk_double(state);
+}
+
+double rk_standard_gamma(rk_state *state, double shape)
+{
+ double b, c;
+ double U, V, X, Y;
+
+ if (shape == 1.0)
+ {
+ return rk_standard_exponential(state);
+ }
+ else if (shape < 1.0)
+ {
+ for (;;)
+ {
+ U = rk_double(state);
+ V = rk_standard_exponential(state);
+ if (U <= 1.0 - shape)
+ {
+ X = pow(U, 1./shape);
+ if (X <= V)
+ {
+ return X;
+ }
+ }
+ else
+ {
+ Y = -log((1-U)/shape);
+ X = pow(1.0 - shape + shape*Y, 1./shape);
+ if (X <= (V + Y))
+ {
+ return X;
+ }
+ }
+ }
+ }
+ else
+ {
+ b = shape - 1./3.;
+ c = 1./sqrt(9*b);
+ for (;;)
+ {
+ do
+ {
+ X = rk_gauss(state);
+ V = 1.0 + c*X;
+ } while (V <= 0.0);
+
+ V = V*V*V;
+ U = rk_double(state);
+ if (U < 1.0 - 0.0331*(X*X)*(X*X)) return (b*V);
+ if (log(U) < 0.5*X*X + b*(1. - V + log(V))) return (b*V);
+ }
+ }
+}
+
+double rk_gamma(rk_state *state, double shape, double scale)
+{
+ return scale * rk_standard_gamma(state, shape);
+}
+
+double rk_beta(rk_state *state, double a, double b)
+{
+ double Ga, Gb;
+
+ if ((a <= 1.0) && (b <= 1.0))
+ {
+ double U, V, X, Y;
+ /* Use Jonk's algorithm */
+
+ while (1)
+ {
+ U = rk_double(state);
+ V = rk_double(state);
+ X = pow(U, 1.0/a);
+ Y = pow(V, 1.0/b);
+
+ if ((X + Y) <= 1.0)
+ {
+ return X;
+ }
+ }
+ }
+ else
+ {
+ Ga = rk_standard_gamma(state, a);
+ Gb = rk_standard_gamma(state, b);
+ return Ga/(Ga + Gb);
+ }
+}
+
+double rk_chisquare(rk_state *state, double df)
+{
+ return 2.0*rk_standard_gamma(state, df/2.0);
+}
+
+double rk_noncentral_chisquare(rk_state *state, double df, double nonc)
+{
+ double Chi2, N;
+
+ Chi2 = rk_chisquare(state, df-1);
+ N = rk_gauss(state) + sqrt(nonc);
+ return Chi2 + N*N;
+}
+
+double rk_f(rk_state *state, double dfnum, double dfden)
+{
+ return rk_chisquare(state, dfnum) / rk_chisquare(state, dfden);
+}
+
+double rk_noncentral_f(rk_state *state, double dfnum, double dfden, double nonc)
+{
+ return ((rk_noncentral_chisquare(state, dfnum, nonc)*dfden) /
+ (rk_chisquare(state, dfden)*dfnum));
+}
+
+long rk_binomial_btpe(rk_state *state, long n, double p)
+{
+ double r,q,fm,p1,xm,xl,xr,c,laml,lamr,p2,p3,p4;
+ double a,u,v,s,F,rho,t,A,nrq,x1,x2,f1,f2,z,z2,w,w2,x;
+ long m,y,k,i;
+
+ if (!(state->has_binomial) ||
+ (state->nsave != n) ||
+ (state->psave != p))
+ {
+ /* initialize */
+ state->nsave = n;
+ state->psave = p;
+ state->has_binomial = 1;
+ state->r = r = min(p, 1.0-p);
+ state->q = q = 1.0 - r;
+ state->fm = fm = n*r+r;
+ state->m = m = (long)floor(state->fm);
+ state->p1 = p1 = floor(2.195*sqrt(n*r*q)-4.6*q) + 0.5;
+ state->xm = xm = m + 0.5;
+ state->xl = xl = xm - p1;
+ state->xr = xr = xm + p1;
+ state->c = c = 0.134 + 20.5/(15.3 + m);
+ a = (fm - xl)/(fm-xl*r);
+ state->laml = laml = a*(1.0 + a/2.0);
+ a = (xr - fm)/(xr*q);
+ state->lamr = lamr = a*(1.0 + a/2.0);
+ state->p2 = p2 = p1*(1.0 + 2.0*c);
+ state->p3 = p3 = p2 + c/laml;
+ state->p4 = p4 = p3 + c/lamr;
+ }
+ else
+ {
+ r = state->r;
+ q = state->q;
+ fm = state->fm;
+ m = state->m;
+ p1 = state->p1;
+ xm = state->xm;
+ xl = state->xl;
+ xr = state->xr;
+ c = state->c;
+ laml = state->laml;
+ lamr = state->lamr;
+ p2 = state->p2;
+ p3 = state->p3;
+ p4 = state->p4;
+ }
+
+ /* sigh ... */
+ Step10:
+ nrq = n*r*q;
+ u = rk_double(state)*p4;
+ v = rk_double(state);
+ if (u > p1) goto Step20;
+ y = (long)floor(xm - p1*v + u);
+ goto Step60;
+
+ Step20:
+ if (u > p2) goto Step30;
+ x = xl + (u - p1)/c;
+ v = v*c + 1.0 - fabs(m - x + 0.5)/p1;
+ if (v > 1.0) goto Step10;
+ y = (long)floor(x);
+ goto Step50;
+
+ Step30:
+ if (u > p3) goto Step40;
+ y = (long)floor(xl + log(v)/laml);
+ if (y < 0) goto Step10;
+ v = v*(u-p2)*laml;
+ goto Step50;
+
+ Step40:
+ y = (int)floor(xr - log(v)/lamr);
+ if (y > n) goto Step10;
+ v = v*(u-p3)*lamr;
+
+ Step50:
+ k = fabs(y - m);
+ if ((k > 20) && (k < ((nrq)/2.0 - 1))) goto Step52;
+
+ s = r/q;
+ a = s*(n+1);
+ F = 1.0;
+ if (m < y)
+ {
+ for (i=m; i<=y; i++)
+ {
+ F *= (a/i - s);
+ }
+ }
+ else if (m > y)
+ {
+ for (i=y; i<=m; i++)
+ {
+ F /= (a/i - s);
+ }
+ }
+ else
+ {
+ if (v > F) goto Step10;
+ goto Step60;
+ }
+
+ Step52:
+ rho = (k/(nrq))*((k*(k/3.0 + 0.625) + 0.16666666666666666)/nrq + 0.5);
+ t = -k*k/(2*nrq);
+ A = log(v);
+ if (A < (t - rho)) goto Step60;
+ if (A > (t + rho)) goto Step10;
+
+ x1 = y+1;
+ f1 = m+1;
+ z = n+1-m;
+ w = n-y+1;
+ x2 = x1*x1;
+ f2 = f1*f1;
+ z2 = z*z;
+ w2 = w*w;
+ if (A > (xm*log(f1/x1)
+ + (n-m+0.5)*log(z/w)
+ + (y-m)*log(w*r/(x1*q))
+ + (13680.-(462.-(132.-(99.-140./f2)/f2)/f2)/f2)/f1/166320.
+ + (13680.-(462.-(132.-(99.-140./z2)/z2)/z2)/z2)/z/166320.
+ + (13680.-(462.-(132.-(99.-140./x2)/x2)/x2)/x2)/x1/166320.
+ + (13680.-(462.-(132.-(99.-140./w2)/w2)/w2)/w2)/w/166320.))
+ {
+ goto Step10;
+ }
+
+ Step60:
+ if (p > 0.5)
+ {
+ y = n - y;
+ }
+
+ return y;
+}
+
+long rk_binomial_inversion(rk_state *state, long n, double p)
+{
+ double q, qn, np, px, U;
+ long X, bound;
+
+ if (!(state->has_binomial) ||
+ (state->nsave != n) ||
+ (state->psave != p))
+ {
+ state->nsave = n;
+ state->psave = p;
+ state->has_binomial = 1;
+ state->q = q = 1.0 - p;
+ state->r = qn = exp(n * log(q));
+ state->c = np = n*p;
+ state->m = bound = min(n, np + 10.0*sqrt(np));
+ } else
+ {
+ q = state->q;
+ qn = state->r;
+ np = state->c;
+ bound = state->m;
+ }
+ X = 0;
+ px = qn;
+ U = rk_double(state);
+ while (U > px)
+ {
+ X++;
+ if (X > bound)
+ {
+ X = 0;
+ px = qn;
+ U = rk_double(state);
+ } else
+ {
+ U -= px;
+ px = ((n-X+1) * p * px)/(X*q);
+ }
+ }
+ return X;
+}
+
+long rk_binomial(rk_state *state, long n, double p)
+{
+ double q;
+
+ if (p <= 0.5)
+ {
+ if (p*n <= 30.0)
+ {
+ return rk_binomial_inversion(state, n, p);
+ }
+ else
+ {
+ return rk_binomial_btpe(state, n, p);
+ }
+ }
+ else
+ {
+ q = 1.0-p;
+ if (q*n <= 30.0)
+ {
+ return n - rk_binomial_inversion(state, n, q);
+ }
+ else
+ {
+ return n - rk_binomial_btpe(state, n, q);
+ }
+ }
+
+}
+
+long rk_negative_binomial(rk_state *state, long n, double p)
+{
+ double Y;
+
+ Y = rk_gamma(state, n, (1-p)/p);
+ return rk_poisson(state, Y);
+}
+
+long rk_poisson_mult(rk_state *state, double lam)
+{
+ long X;
+ double prod, U, enlam;
+
+ enlam = exp(-lam);
+ X = 0;
+ prod = 1.0;
+ while (1)
+ {
+ U = rk_double(state);
+ prod *= U;
+ if (prod > enlam)
+ {
+ X += 1;
+ }
+ else
+ {
+ return X;
+ }
+ }
+}
+
+#define LS2PI 0.91893853320467267
+#define TWELFTH 0.083333333333333333333333
+long rk_poisson_ptrs(rk_state *state, double lam)
+{
+ long k;
+ double U, V, slam, loglam, a, b, invalpha, vr, us;
+
+ slam = sqrt(lam);
+ loglam = log(lam);
+ b = 0.931 + 2.53*slam;
+ a = -0.059 + 0.02483*b;
+ invalpha = 1.1239 + 1.1328/(b-3.4);
+ vr = 0.9277 - 3.6224/(b-2);
+
+ while (1)
+ {
+ U = rk_double(state) - 0.5;
+ V = rk_double(state);
+ us = 0.5 - fabs(U);
+ k = (long)floor((2*a/us + b)*U + lam + 0.43);
+ if ((us >= 0.07) && (V <= vr))
+ {
+ return k;
+ }
+ if ((k < 0) ||
+ ((us < 0.013) && (V > us)))
+ {
+ continue;
+ }
+ if ((log(V) + log(invalpha) - log(a/(us*us)+b)) <=
+ (-lam + k*loglam - loggam(k+1)))
+ {
+ return k;
+ }
+
+
+ }
+
+}
+
+long rk_poisson(rk_state *state, double lam)
+{
+ if (lam >= 10)
+ {
+ return rk_poisson_ptrs(state, lam);
+ }
+ else
+ {
+ return rk_poisson_mult(state, lam);
+ }
+}
+
+double rk_standard_cauchy(rk_state *state)
+{
+ return rk_gauss(state) / rk_gauss(state);
+}
+
+double rk_standard_t(rk_state *state, double df)
+{
+ double N, G, X;
+
+ N = rk_gauss(state);
+ G = rk_standard_gamma(state, df/2);
+ X = sqrt(df/2)*N/sqrt(G);
+ return X;
+}
+
+double rk_vonmises(rk_state *state, double mu, double kappa)
+{
+ double r, rho, s;
+ double U, V, W, Y, Z;
+ double result, mod;
+
+ if (kappa < 1e-8)
+ {
+ return M_PI * (2*rk_double(state)-1);
+ }
+ else
+ {
+ r = 1 + sqrt(1 + 4*kappa*kappa);
+ rho = (r - sqrt(2*r))/(2*kappa);
+ s = (1 + rho*rho)/(2*rho);
+
+ while (1)
+ {
+ U = 2*rk_double(state) - 1;
+ V = 2*rk_double(state) - 1;
+ Z = cos(M_PI*U);
+ W = (1 + s*Z)/(s + Z);
+ Y = kappa * (s - W);
+ if ((Y*(2-Y) - V >= 0) || (log(Y/V)+1 - Y >= 0))
+ {
+ break;
+ }
+ }
+
+ if (U < 0)
+ {
+ result = acos(W);
+ }
+ else
+ {
+ result = -acos(W);
+ }
+ result += mu + M_PI;
+ mod = fmod(result, 2*M_PI);
+ if (mod && (mod < 0))
+ {
+ mod += 2*M_PI;
+ }
+ return mod - M_PI;
+ }
+}
+
+double rk_pareto(rk_state *state, double a)
+{
+ return exp(rk_standard_exponential(state)/a) - 1;
+}
+
+double rk_weibull(rk_state *state, double a)
+{
+ return pow(rk_standard_exponential(state), 1./a);
+}
+
+double rk_power(rk_state *state, double a)
+{
+ return pow(1 - exp(-rk_standard_exponential(state)), 1./a);
+}
+
+double rk_laplace(rk_state *state, double loc, double scale)
+{
+ double U;
+
+ U = rk_double(state);
+ if (U < 0.5)
+ {
+ U = loc + scale * log(U + U);
+ } else
+ {
+ U = loc - scale * log(2.0 - U - U);
+ }
+ return U;
+}
+
+double rk_gumbel(rk_state *state, double loc, double scale)
+{
+ double U;
+
+ U = 1.0 - rk_double(state);
+ return loc - scale * log(-log(U));
+}
+
+double rk_logistic(rk_state *state, double loc, double scale)
+{
+ double U;
+
+ U = rk_double(state);
+ return loc + scale * log(U/(1.0 - U));
+}
+
+double rk_lognormal(rk_state *state, double mean, double sigma)
+{
+ return exp(rk_normal(state, mean, sigma));
+}
+
+double rk_rayleigh(rk_state *state, double mode)
+{
+ return mode*sqrt(-2.0 * log(1.0 - rk_double(state)));
+}
+
+double rk_wald(rk_state *state, double mean, double scale)
+{
+ double U, X, Y;
+ double mu_2l;
+
+ mu_2l = mean / (2*scale);
+ Y = rk_gauss(state);
+ Y = mean*Y*Y;
+ X = mean + mu_2l*(Y - sqrt(4*scale*Y + Y*Y));
+ U = rk_double(state);
+ if (U <= mean/(mean+X))
+ {
+ return X;
+ } else
+ {
+ return mean*mean/X;
+ }
+}
+
+long rk_zipf(rk_state *state, double a)
+{
+ double T, U, V;
+ long X;
+ double b;
+
+ b = pow(2.0, a-1.0);
+ do
+ {
+ U = rk_double(state);
+ V = rk_double(state);
+ X = (long)floor(pow(U, -1.0/(a-1.0)));
+ T = pow(1.0 + 1.0/X, a-1.0);
+ } while ((V *X*(T-1.0)/(b-1.0)) > (T/b));
+ return X;
+}
+
+long rk_geometric_search(rk_state *state, double p)
+{
+ double U;
+ long X;
+ double sum, prod, q;
+
+ X = 1;
+ sum = prod = p;
+ q = 1.0 - p;
+ U = rk_double(state);
+ while (U > sum)
+ {
+ prod *= q;
+ sum += prod;
+ X++;
+ }
+ return X;
+}
+
+long rk_geometric_inversion(rk_state *state, double p)
+{
+ return (long)ceil(log(1.0-rk_double(state))/log(1.0-p));
+}
+
+long rk_geometric(rk_state *state, double p)
+{
+ if (p >= 0.333333333333333333333333)
+ {
+ return rk_geometric_search(state, p);
+ } else
+ {
+ return rk_geometric_inversion(state, p);
+ }
+}
+
+long rk_hypergeometric_hyp(rk_state *state, long good, long bad, long sample)
+{
+ long d1, K, Z;
+ double d2, U, Y;
+
+ d1 = bad + good - sample;
+ d2 = (double)min(bad, good);
+
+ Y = d2;
+ K = sample;
+ while (Y > 0.0)
+ {
+ U = rk_double(state);
+ Y -= (long)floor(U + Y/(d1 + K));
+ K--;
+ if (K == 0) break;
+ }
+ Z = (long)(d2 - Y);
+ if (bad > good) Z = sample - Z;
+ return Z;
+}
+
+/* D1 = 2*sqrt(2/e) */
+/* D2 = 3 - 2*sqrt(3/e) */
+#define D1 1.7155277699214135
+#define D2 0.8989161620588988
+long rk_hypergeometric_hrua(rk_state *state, long good, long bad, long sample)
+{
+ long mingoodbad, maxgoodbad, popsize, m, d9;
+ double d4, d5, d6, d7, d8, d10, d11;
+ long Z;
+ double T, W, X, Y;
+
+ mingoodbad = min(good, bad);
+ popsize = good + bad;
+ maxgoodbad = max(good, bad);
+ m = min(sample, popsize - sample);
+ d4 = ((double)mingoodbad) / popsize;
+ d5 = 1.0 - d4;
+ d6 = m*d4 + 0.5;
+ d7 = sqrt((popsize - m) * sample * d4 *d5 / (popsize-1) + 0.5);
+ d8 = D1*d7 + D2;
+ d9 = (long)floor((double)((m+1)*(mingoodbad+1))/(popsize+2));
+ d10 = (loggam(d9+1) + loggam(mingoodbad-d9+1) + loggam(m-d9+1) +
+ loggam(maxgoodbad-m+d9+1));
+ d11 = min(min(m, mingoodbad)+1.0, floor(d6+16*d7));
+ /* 16 for 16-decimal-digit precision in D1 and D2 */
+
+ while (1)
+ {
+ X = rk_double(state);
+ Y = rk_double(state);
+ W = d6 + d8*(Y- 0.5)/X;
+
+ /* fast rejection: */
+ if ((W < 0.0) || (W >= d11)) continue;
+
+ Z = (long)floor(W);
+ T = d10 - (loggam(Z+1) + loggam(mingoodbad-Z+1) + loggam(m-Z+1) +
+ loggam(maxgoodbad-m+Z+1));
+
+ /* fast acceptance: */
+ if ((X*(4.0-X)-3.0) <= T) break;
+
+ /* fast rejection: */
+ if (X*(X-T) >= 1) continue;
+
+ if (2.0*log(X) <= T) break; /* acceptance */
+ }
+
+ /* this is a correction to HRUA* by Ivan Frohne in rv.py */
+ if (bad > good) Z = m - Z;
+
+ /* another fix from rv.py to allow sample to exceed popsize/2 */
+ if (m < sample) Z = bad - Z;
+
+ return Z;
+}
+#undef D1
+#undef D2
+
+long rk_hypergeometric(rk_state *state, long good, long bad, long sample)
+{
+ if (sample > 10)
+ {
+ return rk_hypergeometric_hrua(state, good, bad, sample);
+ } else
+ {
+ return rk_hypergeometric_hyp(state, good, bad, sample);
+ }
+}
+
+double rk_triangular(rk_state *state, double left, double mode, double right)
+{
+ double base, leftbase, ratio, leftprod, rightprod;
+ double U;
+
+ base = right - left;
+ leftbase = mode - left;
+ ratio = leftbase / base;
+ leftprod = leftbase*base;
+ rightprod = (right - mode)*base;
+
+ U = rk_double(state);
+ if (U <= ratio)
+ {
+ return left + sqrt(U*leftprod);
+ } else
+ {
+ return right - sqrt((1.0 - U) * rightprod);
+ }
+}
+
+long rk_logseries(rk_state *state, double p)
+{
+ double q, r, U, V;
+
+ r = log(1.0 - p);
+
+ V = rk_double(state);
+ if (V >= p) return 1;
+ U = rk_double(state);
+ q = 1.0 - exp(r*U);
+ if (V <= q*q) return (long)floor(1 + log(V)/log(q));
+ if (V <= q) return 1;
+ return 2;
+}