static double
gamma_large_dev3(const double a, gsl_rng *r2)
{
    /* Works only if a > 1, and is most efficient if a is large

       This algorithm, reported in Knuth, is attributed to Ahrens.
       A faster one, we are told, can be found in: J. H. Ahrens and
       U. Dieter, Computing 12 (1974) 223-246.  */

    double          sqa, x, y, v;

    sqa = sqrt((2.0 * a) - 1.0);

    do
    {
        do
        {
            y = tan(MY_PI * gsl_rng_uniform(r2));
            x = (sqa * y) + a - 1.0;
        }
        while (x <= 0.0);

        v = gsl_rng_uniform(r2);
    }
    while (v > (1.0 + y*y) * exp((a - 1.0) * log(x / (a - 1.0)) - (sqa * y)));

    return (x);
}


static double
gamma_int_dev3(const unsigned int a, gsl_rng *r2)
{
    unsigned int    i;
    double          prod;

    if (a < 12)
    {
        prod  = 1.0;
        for (i = 0; i < a; ++i)
            prod *= gsl_rng_uniform(r2);

        /* Note: for 12 iterations we are safe against underflow, since
           the smallest positive random number is O(2^-32). This means
           the smallest possible product is 2^(-12*32) = 10^-116 which
           is within the range of const double precision. */

        return (-log(prod));
    }
    else
    {
        return (gamma_large_dev3(a, r2));
    }
}


static double
gamma_frac_dev3(const double a, gsl_rng *r2)
{
    /* This is exercise 16 from Knuth; see page 135, and the solution is
       on page 551.  */
    double          p, q, x, u, v;

    p = MY_E / (a + MY_E);

    do
    {
        u = gsl_rng_uniform(r2);
        v = gsl_rng_uniform(r2);

        if (u < p)
        {
            x = exp((1.0 / a) * log(v));
            q = exp(-x);
        }
        else
        {
            x = 1.0 - log(v);
            q = exp((a - 1.0) * log(x));
        }
    }
    while (gsl_rng_uniform(r2) >= q);

    return (x);
}


static double
gamma_dev3(const double b, const double c, gsl_rng *r2)
{
   /* assume a > 0 */
   int nc = floor (c);

   if (c == nc)
       return (b * gamma_int_dev3(nc, r2));
   else if (nc == 0.0)
       return (b * gamma_frac_dev3(c, r2));
   else
       return (b * (gamma_int_dev3(nc, r2) + gamma_frac_dev3(c - nc, r2)));
}


static double
invgamma_dev3(const double b, const double c, gsl_rng *r2)
{
    return(1.0 / gamma_dev3(1.0/b, c, r2));
}