distribution 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,63 @@
1
+ module Distribution
2
+ module MathExtension
3
+ # From GSL-1.9.
4
+ module ExponentialIntegral
5
+ class << self
6
+ def first_order x, scale = 0, with_error = false
7
+ xmaxt = -Math::LOG_FLOAT_MIN
8
+ xmax = xmaxt - Math.log(xmaxt)
9
+ result = nil
10
+ error = with_error ? nil : 0.0
11
+
12
+ if x < -xmax && !scale
13
+ raise("Overflow Error")
14
+ elsif x <= -10.0
15
+ s = 1.0 / x * ( scale ? 1.0 : Math.exp(-x))
16
+ result_c = ChebyshevSeries.eval(20.0/x+1.0, :ae11, with_error)
17
+ result_c, result_c_err = result_c if with_error
18
+ result = s * (1.0 + result_c)
19
+ error ||= (s * result_c_err) + 2.0*Float::EPSILON * (x.abs + 1.0) * result.abs
20
+ elsif x <= -4.0
21
+ s = 1.0 / x * (scale ? 1.0 : Math.exp(-x))
22
+ result_c = ChebyshevSeries.eval((40.0/x+7.0)/3.0, :ae12, with_error)
23
+ result_c, result_c_err = result_c if with_error
24
+ result = s * (1.0 + result_c)
25
+ error ||= (s * result_c_err) + 2.0*Float::EPSILON * result.abs
26
+ elsif x <= -1.0
27
+ ln_term = - Math.log(x.abs)
28
+ scale_factor = scale ? Math.exp(x) : 1.0
29
+ result_c = ChebyshevSeries.eval((2.0*x+5.0)/3.0, :e11, with_error)
30
+ result_c, result_c_err = result_c if with_error
31
+ result = scale_factor * (ln_term + result_c)
32
+ error ||= scale_factor * (result_c_err + Float::EPSILON * ln_term.abs) + 2.0*Float::EPSILON*result.abs
33
+ elsif x == 0.0
34
+ raise(ArgumentError, "Domain Error")
35
+ elsif x <= 1.0
36
+ ln_term = - Math.log(x.abs)
37
+ scale_factor = scale ? Math.exp(x) : 1.0
38
+ result_c = ChebyshevSeries.eval(x, :e12, with_error)
39
+ result_c, result_c_err = result_c if with_error
40
+ result = scale_factor * (ln_term - 0.6875 + x + result_c)
41
+ error ||= scale_factor * (result_c_err + Float::EPSILON * ln_term.abs) + 2.0*Float::EPSILON*result.abs
42
+ elsif x <= 4.0
43
+ s = 1.0 / x * (scale ? 1.0 : Math.exp(-x))
44
+ result_c = ChebyshevSeries.eval((8.0/x-5.0)/3.0, :ae13, with_error)
45
+ result_c, result_c_err = result_c if with_error
46
+ result = s * (1.0 + result_c)
47
+ error ||= (s * result_c_err) + 2.0*Float::EPSILON * result.abs
48
+ elsif x <= xmax || scale
49
+ s = 1.0 / x * (scale ? 1.0 : Math.exp(-x))
50
+ result_c = ChebyshevSeries.eval(8.0/x-1.0, :ae14, with_error)
51
+ result_c, result_c_err = result_c if with_error
52
+ result = s * (1.0 + result_c)
53
+ error ||= s * (Float::EPSILON + result_c_err) + 2.0*(x+1.0)*Float::EPSILON * result.abs
54
+ raise("Underflow Error") if result == 0.0
55
+ else
56
+ raise("Underflow Error")
57
+ end
58
+ with_error ? [result, error] : result
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,64 @@
1
+ # Added by John O. Woods, SciRuby project.
2
+ # Derived from GSL-1.9 source files in the specfunc/ dir.
3
+
4
+ module Distribution
5
+ module MathExtension
6
+ # Derived from GSL-1.9.
7
+ module Gammastar
8
+ C0 = 1.quo(12)
9
+ C1 = -1.quo(360)
10
+ C2 = 1.quo(1260)
11
+ C3 = -1.quo(1680)
12
+ C4 = 1.quo(1188)
13
+ C5 = -691.quo(360360)
14
+ C6 = 1.quo(156)
15
+ C7 = -3617.quo(122400)
16
+
17
+ class << self
18
+
19
+ def series x, with_error = false
20
+ # Use the Stirling series for the correction to Log(Gamma(x)),
21
+ # which is better behaved and easier to compute than the
22
+ # regular Stirling series for Gamma(x).
23
+ y = 1.quo(x*x)
24
+ ser = C0 + y*(C1 + y*(C2 + y*(C3 + y*(C4 + y*(C5 + y*(C6 + y*C7))))))
25
+ result = Math.exp(ser/x)
26
+ with_error ? [result, 2.0 * Float::EPSILON * result * [1, ser/x].max] : result
27
+ end
28
+
29
+ def evaluate x, with_error = false
30
+ raise(ArgumentError, "x must be positive") if x <= 0
31
+ if x < 0.5
32
+ STDERR.puts("Warning: Don't know error on lg_x, error for this function will be incorrect") if with_error
33
+ lg = Math.lgamma(x).first
34
+ lg_err = Float::EPSILON # Guess
35
+ lx = Math.log(x)
36
+ c = 0.5 * (LN2 + LNPI)
37
+ lnr_val = lg - (x-0.5)*lx + x - c
38
+ lnr_err = lg_err + 2.0*Float::EPSILON * ((x+0.5)*lx.abs + c)
39
+ with_error ? exp_err(lnr_val, lnr_err) : Math.exp(lnr_val)
40
+ elsif x < 2.0
41
+ t = 4.0/3.0*(x-0.5) - 1.0
42
+ ChebyshevSeries.evaluate(:gstar_a, t, with_error)
43
+ elsif x < 10.0
44
+ t = 0.25*(x-2.0) - 1.0
45
+ c = ChebyshevSeries.evaluate(:gstar_b, t, with_error)
46
+ c, c_err = c if with_error
47
+
48
+ result = c / (x*x) + 1.0 + 1.0/(12.0*x)
49
+ with_error ? [result, c_err / (x*x) + 2.0*Float::EPSILON*result.abs] : result
50
+ elsif x < 1.0/Math::ROOT4_FLOAT_EPSILON
51
+ series x, with_error
52
+ elsif x < 1.0 / Float::EPSILON # Stirling
53
+ xi = 1.0 / x
54
+ result = 1.0 + xi/12.0*(1.0 + xi/24.0*(1.0 - xi*(139.0/180.0 + 571.0/8640.0*xi)))
55
+ result_err = 2.0 * Float::EPSILON * result.abs
56
+ with_error ? [result,result_err] : result
57
+ else
58
+ with_error ? [1.0,1.0/x] : 1.0
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,31 @@
1
+ # Added by John O. Woods, SciRuby project.
2
+ # Derived from GSL-1.9 source files, mostly in the specfunc/ dir.
3
+
4
+ module Distribution
5
+ module MathExtension
6
+ LNPI = 1.14472988584940017414342735135
7
+ LN2 = 0.69314718055994530941723212146
8
+ SQRT2 = 1.41421356237309504880168872421
9
+ SQRTPI = 1.77245385090551602729816748334
10
+
11
+ ROOT3_FLOAT_MIN = Float::MIN ** (1/3.0)
12
+ ROOT3_FLOAT_EPSILON = Float::EPSILON ** (1/3.0)
13
+ ROOT4_FLOAT_MIN = Float::MIN ** (1/4.0)
14
+ ROOT4_FLOAT_EPSILON = Float::EPSILON ** (1/4.0)
15
+ ROOT5_FLOAT_MIN = Float::MIN ** (1/5.0)
16
+ ROOT5_FLOAT_EPSILON = Float::EPSILON ** (1/5.0)
17
+ ROOT6_FLOAT_MIN = Float::MIN ** (1/6.0)
18
+ ROOT6_FLOAT_EPSILON = Float::EPSILON ** (1/6.0)
19
+ LOG_FLOAT_MIN = Math.log(Float::MIN)
20
+ EULER = 0.57721566490153286060651209008
21
+
22
+ # e^x taking into account the error thus far (I think)
23
+ # gsl_sf_exp_err_e
24
+ def exp_err(x, dx)
25
+ adx = dx.abs
26
+ raise("Overflow Error in exp_err: x + adx > LOG_FLOAT_MAX") if x + adx > LOG_FLOAT_MAX
27
+ raise("Underflow Error in exp_err: x - adx < LOG_FLOAT_MIN") if x - adx < LOG_FLOAT_MIN
28
+ [Math.exp(x), Math.exp(x) * [Float::EPSILON, Math.exp(adx) - 1.0/Math.exp(adx)] + 2.0 * Float::EPSILON * Math.exp(x).abs]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,213 @@
1
+ # Added by John O. Woods, SciRuby project.
2
+ # Derived from GSL-1.9 source files in the specfunc/ dir.
3
+
4
+ module Distribution
5
+ module MathExtension
6
+ module Beta
7
+ class << self
8
+ # Based on gsl_sf_lnbeta_e and gsl_sf_lnbeta_sgn_e
9
+ # Returns result and sign in an array. If with_error is specified, also returns the error.
10
+ def log_beta(x,y, with_error=false)
11
+ sign = nil
12
+
13
+ raise(ArgumentError, "x and y must be nonzero") if x == 0.0 || y == 0.0
14
+ raise(ArgumentError, "not defined for negative integers") if [x,y].any? { |v| (v.is_a?(Fixnum) && v < 0) }
15
+
16
+ # See if we can handle the positive case with min/max < 0.2
17
+ if x > 0 && y > 0
18
+ min, max = [x,y].minmax
19
+ ratio = min.quo(max)
20
+
21
+ if ratio < 0.2
22
+ gsx = Gammastar.evaluate(x, with_error)
23
+ gsy = Gammastar.evaluate(y, with_error)
24
+ gsxy = Gammastar.evaluate(x+y, with_error)
25
+ lnopr = Log::log_1plusx(ratio, with_error)
26
+
27
+ gsx, gsx_err, gsy, gsy_err, gsxy, gsxy_err, lnopr, lnopr_err = [gsx,gsy,gsxy,lnopr].flatten if with_error
28
+
29
+ lnpre = Math.log((gsx*gsy).quo(gsxy) * Math::SQRT2 * Math::SQRTPI)
30
+ lnpre_err = gsx_err.quo(gsx) + gsy_err(gsy) + gsxy_err.quo(gsxy) if with_error
31
+
32
+ t1 = min * Math.log(ratio)
33
+ t2 = 0.5 * Math.log(min)
34
+ t3 = (x+y-0.5)*lnopr
35
+
36
+ lnpow = t1 - t2 - t3
37
+ lnpow_err = Float::EPSILON * (t1.abs + t2.abs + t3.abs) + (x+y-0.5).abs * lnopr_err if with_error
38
+
39
+ result = lnpre + lnpow
40
+ error = lnpre_err + lnpow_err + 2.0*Float::EPSILON*result.abs if with_error
41
+
42
+ return with_error ? [result, 1.0, error] : [result, 1.0]
43
+ end
44
+ end
45
+
46
+ # General case: fallback
47
+ lgx, sgx = Math.lgamma(x)
48
+ lgy, sgy = Math.lgamma(y)
49
+ lgxy, sgxy = Math.lgamma(x+y)
50
+ sgn = sgx * sgy * sgxy
51
+
52
+ raise("Domain error: sign is -") if sgn == -1
53
+
54
+ result = lgx + lgy - lgxy
55
+ if with_error
56
+ lgx_err, lgy_err, lgxy_err = begin
57
+ STDERR.puts("Warning: Error is unknown for Math::lgamma, guessing.")
58
+ [Math::EPSILON, Math::EPSILON, Math::EPSILON]
59
+ end
60
+
61
+ error = lgx_err + lgy_err + lgxy_err + Float::EPSILON*(lgx.abs+lgy.abs+lgxy.abs) + 2.0*(Float::EPSILON)*result.abs
62
+ return [result, sgn, error]
63
+ else
64
+ return [result, sgn]
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+ # Calculate regularized incomplete beta function
71
+ module IncompleteBeta
72
+
73
+ MAX_ITER = 512
74
+ CUTOFF = 2.0 * Float::MIN
75
+
76
+ class << self
77
+
78
+ # Evaluate aa * beta_inc(a,b,x) + yy
79
+ #
80
+ # No error mode available.
81
+ #
82
+ # From GSL-1.9: cdf/beta_inc.c, beta_inc_AXPY
83
+ def axpy(aa,yy,a,b,x)
84
+ return aa*0 + yy if x == 0.0
85
+ return aa*1 + yy if x == 1.0
86
+
87
+ ln_beta = Math.logbeta(a, b)
88
+ ln_pre = -ln_beta + a * Math.log(x) + b * Math::Log.log1p(-x)
89
+ prefactor = Math.exp(ln_pre)
90
+
91
+ if x < (a+1).quo(a+b+2)
92
+ # Apply continued fraction directly
93
+ epsabs = yy.quo((aa * prefactor).quo(a)).abs * Float::EPSILON
94
+ cf = continued_fraction(a, b, x, epsabs)
95
+ return aa * (prefactor * cf).quo(a) + yy
96
+ else
97
+ # Apply continued fraction after hypergeometric transformation
98
+ epsabs = (aa + yy).quo( (aa*prefactor).quo(b) ) * Float::EPSILON
99
+ cf = continued_fraction(b, a, 1-x, epsabs)
100
+ term = (prefactor * cf).quo(b)
101
+ return aa == -yy ? -aa*term : aa*(1-term)+yy
102
+ end
103
+ end
104
+
105
+
106
+ # Evaluate the incomplete beta function
107
+ # gsl_sf_beta_inc_e
108
+ def evaluate(a,b,x,with_error=false)
109
+ raise(ArgumentError, "Domain error: a(#{a}), b(#{b}) must be positive; x(#{x}) must be between 0 and 1, inclusive") if a <= 0 || b <= 0 || x < 0 || x > 1
110
+ if x == 0
111
+ return with_error ? [0.0,0.0] : 0.0
112
+ elsif x == 1
113
+ return with_error ? [1.0,0.0] : 1.0
114
+ else
115
+
116
+ ln_beta = Beta.log_beta(a,b, with_error)
117
+ ln_1mx = Log.log_1plusx(-x, with_error)
118
+ ln_x = Math.log(x)
119
+
120
+ ln_beta, ln_beta_err, ln_1mx, ln_1mx_err, ln_x_err = begin
121
+ #STDERR.puts("Warning: Error is unknown for Math::log, guessing.")
122
+ [ln_beta,ln_1mx,Float::EPSILON].flatten
123
+ end
124
+
125
+ ln_pre = -ln_beta + a*ln_x + b*ln_1mx
126
+ ln_pre_err = ln_beta_err + (a*ln_x_err).abs + (b*ln_1mx_err).abs if with_error
127
+
128
+ prefactor, prefactor_err = begin
129
+ if with_error
130
+ exp_err(ln_pre, ln_pre_err)
131
+ else
132
+ [Math.exp(ln_pre), nil]
133
+ end
134
+ end
135
+
136
+ if x < (a+1).quo(a+b+2)
137
+ # Apply continued fraction directly
138
+
139
+ cf = continued_fraction(a,b,x, nil, with_error)
140
+ cf,cf_err = cf if with_error
141
+ result = (prefactor * cf).quo(a)
142
+ return with_error ? [result, ((prefactor_err*cf).abs + (prefactor*cf_err).abs).quo(a)] : result
143
+ else
144
+ # Apply continued fraction after hypergeometric transformation
145
+
146
+ cf = continued_fraction(b, a, 1-x, nil)
147
+ cf,cf_err = cf if with_error
148
+ term = (prefactor * cf).quo(b)
149
+ result = 1 - term
150
+
151
+ return with_error ? [result, (prefactor_err*cf).quo(b) + (prefactor*cf_err).quo(b) + 2.0*Float::EPSILON*(1+term.abs)] : result
152
+ end
153
+
154
+ end
155
+ end
156
+
157
+
158
+ def continued_fraction_cutoff(epsabs)
159
+ return CUTOFF if epsabs.nil?
160
+ 0.0/0 # NaN
161
+ end
162
+
163
+ # Continued fraction calculation of incomplete beta
164
+ # beta_cont_frac from GSL-1.9
165
+ #
166
+ # If epsabs is set, will execute the version of the GSL function in the cdf folder. Otherwise, does the
167
+ # basic one in specfunc.
168
+ def continued_fraction(a,b,x,epsabs=nil,with_error=false)
169
+ num_term = 1
170
+ den_term = 1 - (a+b)*x.quo(a+1)
171
+ k = 0
172
+
173
+ den_term = continued_fraction_cutoff(epsabs) if den_term.abs < CUTOFF
174
+ den_term = 1.quo(den_term)
175
+ cf = den_term
176
+
177
+ 1.upto(MAX_ITER) do |k|
178
+ coeff = k *(b-k)*x.quo(((a-1)+2*k)*(a+2*k)) # coefficient for step 1
179
+ delta_frac = nil
180
+ 2.times do
181
+
182
+ den_term = 1 + coeff*den_term
183
+ num_term = 1 + coeff.quo(num_term)
184
+
185
+ den_term = continued_fraction_cutoff(epsabs) if den_term.abs < CUTOFF
186
+ num_term = continued_fraction_cutoff(epsabs) if num_term.abs < CUTOFF
187
+
188
+ den_term = 1.quo(den_term)
189
+
190
+ delta_frac = den_term * num_term
191
+ cf *= delta_frac
192
+
193
+ coeff = -(a+k)*(a+b+k)*x.quo((a+2*k)*(a+2*k+1)) # coefficient for step 2
194
+ end
195
+
196
+ break if (delta_frac-1).abs < 2.0*Float::EPSILON
197
+ break if !epsabs.nil? && (cf * (delta_frac-1).abs < epsabs)
198
+ end
199
+
200
+ if k > MAX_ITER
201
+ raise("Exceeded maximum number of iterations") if epsabs.nil?
202
+ return with_error ? [0.0/0, 0] : 0.0/0 # NaN if epsabs is set
203
+ end
204
+
205
+ with_error ? [cf, k * 4 * Float::EPSILON * cf.abs] : cf
206
+ end
207
+
208
+
209
+ end
210
+
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,424 @@
1
+ # Added by John O. Woods, SciRuby project.
2
+ # Derived from GSL-1.9 source files in the specfunc/ dir.
3
+
4
+ # require "statsample"
5
+
6
+ module Distribution
7
+ module MathExtension
8
+ module IncompleteGamma
9
+ NMAX = 5000
10
+ SMALL = Float::EPSILON ** 3
11
+ PG21 = -2.404113806319188570799476 # PolyGamma[2,1]
12
+
13
+ class << self
14
+
15
+ # Helper function for plot
16
+ #def range_to_array r
17
+ # r << (r.last - r.first)/100.0 if r.size == 2 # set dr as Dr/100.0
18
+ # arr = []
19
+ # pos = r[0]
20
+ # while pos <= r[1]
21
+ # arr << pos
22
+ # pos += r[2]
23
+ # end
24
+ # arr
25
+ #end
26
+ #
27
+ #def plot a, x_range, fun = :p
28
+ # x_range = range_to_array(x_range) if x_range.is_a?(Array)
29
+ # y_range = x_range.collect { |x| self.send(fun, a, x) }
30
+ # graph = Statsample::Graph::Scatterplot.new x_range.to_scale, y_range.to_scale
31
+ # f = File.new("test.svg", "w")
32
+ # f.puts(graph.to_svg)
33
+ # f.close
34
+ # `google-chrome test.svg`
35
+ #end
36
+
37
+ # The dominant part, D(a,x) := x^a e^(-x) / Gamma(a+1)
38
+ # gamma_inc_D in GSL-1.9.
39
+ def d(a, x, with_error = false)
40
+ error = nil
41
+ if a < 10.0
42
+ ln_a = Math.lgamma(a+1.0).first
43
+ lnr = a * Math.log(x) - x - ln_a
44
+ result = Math.exp(lnr)
45
+ error = 2.0 * Float::EPSILON * (lnr.abs + 1.0) + result.abs if with_error
46
+ with_error ? [result,error] : result
47
+ else
48
+ ln_term = ln_term_error = nil
49
+ if x < 0.5*a
50
+ u = x/a.to_f
51
+ ln_u = Math.log(u)
52
+ ln_term = ln_u - u + 1.0
53
+ ln_term_error = (ln_u.abs + u.abs + 1.0) * Float::EPSILON if with_error
54
+ else
55
+ mu = (x-a)/a.to_f
56
+ ln_term = Log::log_1plusx_minusx(mu, with_error)
57
+ ln_term, ln_term_error = ln_term if with_error
58
+ end
59
+ gstar = Gammastar.evaluate(a, with_error)
60
+ gstar,gstar_error = gstar if with_error
61
+ term1 = Math.exp(a*ln_term) / Math.sqrt(2.0*Math::PI*a)
62
+ result = term1/gstar
63
+ error = 2.0*Float::EPSILON*((a*ln_term).abs+1.0) * result.abs + gstar_error/gstar.abs * result.abs if with_error
64
+ with_error ? [result,error] : result
65
+ end
66
+ end
67
+
68
+ # gamma_inc_P_series
69
+ def p_series(a,x,with_error=false)
70
+ d = d(a,x,with_error)
71
+ d, d_err = d if with_error
72
+ sum = 1.0
73
+ term = 1.0
74
+ n = 1
75
+ 1.upto(NMAX-1) do |n|
76
+ term *= x / (a+n).to_f
77
+ sum += term
78
+ break if (term/sum).abs < Float::EPSILON
79
+ end
80
+
81
+ result = d * sum
82
+
83
+ if n == NMAX
84
+ STDERR.puts("Error: n reached NMAX in p series")
85
+ else
86
+ return with_error ? [result,d_err * sum.abs + (1.0+n)*Float::EPSILON * result.abs] : result
87
+ end
88
+ end
89
+
90
+ # This function does not exist in GSL, but is nonetheless GSL code. It's for calculating two specific ranges of p.
91
+ def q_asymptotic_uniform_complement a,x,with_error=false
92
+ q = q_asymptotic_uniform(a, x, with_error)
93
+ q,q_err = q if with_error
94
+ result = 1.0 - q
95
+ return with_error ? [result, q_err + 2.0*Float::EPSILON*result.abs] : result
96
+ end
97
+
98
+ def q_continued_fraction_complement a,x,with_error=false
99
+ q = q_continued_fraction(a,x,with_error)
100
+ return with_error ? [1.0 - q.first, q.last + 2.0*Float::EPSILON*(1.0-q.first).abs] : 1.0 - q
101
+ end
102
+
103
+ def q_large_x_complement a,x,with_error=false
104
+ q = q_large_x(a,x,with_error)
105
+ return with_error ? [1.0 - q.first, q.last + 2.0*Float::EPSILON*(1.0-q.first).abs] : 1.0 - q
106
+ end
107
+
108
+ # The incomplete gamma function.
109
+ # gsl_sf_gamma_inc_P_e
110
+ def p a,x,with_error=false
111
+ raise(ArgumentError, "Range Error: a must be positive, x must be non-negative") if a <= 0.0 || x < 0.0
112
+ if x == 0.0
113
+ return with_error ? [0.0, 0.0] : 0.0
114
+ elsif x < 20.0 || x < 0.5*a
115
+ return p_series(a, x, with_error)
116
+ elsif a > 1e6 && (x-a)*(x-a) < a
117
+ return q_asymptotic_uniform_complement a, x, with_error
118
+ elsif a <= x
119
+ if a > 0.2*x
120
+ return q_continued_fraction_complement(a, x, with_error)
121
+ else
122
+ return q_large_x_complement(a, x, with_error)
123
+ end
124
+ elsif (x-a)*(x-a) < a
125
+ return q_asymptotic_uniform_complement a, x, with_error
126
+ else
127
+ return p_series(a, x, with_error)
128
+ end
129
+ end
130
+
131
+ # gamma_inc_Q_e
132
+ def q a,x,with_error=false
133
+ raise(ArgumentError, "Range Error: a and x must be non-negative") if (a < 0.0 || x < 0.0)
134
+ if x == 0.0
135
+ return with_error ? [1.0, 0.0] : 1.0
136
+ elsif a == 0.0
137
+ return with_error ? [0.0, 0.0] : 0.0
138
+ elsif x <= 0.5*a
139
+ # If series is quick, do that.
140
+ p = p_series(a,x, with_error)
141
+ p,p_err = p if with_error
142
+ result = 1.0 - p
143
+ return with_error ? [result, p_err + 2.0*Float::EPSILON*result.abs] : result
144
+ elsif a >= 1.0e+06 && (x-a)*(x-a) < a # difficult asymptotic regime, only way to do this region
145
+ return q_asymptotic_uniform(a, x, with_error)
146
+ elsif a < 0.2 && x < 5.0
147
+ return q_series(a,x, with_error)
148
+ elsif a <= x
149
+ return x <= 1.0e+06 ? q_continued_fraction(a, x, with_error) : q_large_x(a, x, with_error)
150
+ else
151
+ if x > a-Math.sqrt(a)
152
+ return q_continued_fraction(a, x, with_error)
153
+ else
154
+ p = p_series(a, x, with_error)
155
+ p, p_err = p if with_error
156
+ result = 1.0 - p
157
+ return with_error ? [result, p_err + 2.0*Float::EPSILON*result.abs] : result
158
+ end
159
+ end
160
+ end
161
+
162
+ # gamma_inc_Q_CF
163
+ def q_continued_fraction a, x, with_error=false
164
+ d = d(a, x, with_error)
165
+ f = f_continued_fraction(a, x, with_error)
166
+
167
+ if with_error
168
+ [d.first*(a/x).to_f*f.first, d.last * ((a/x).to_f*f.first).abs + (d.first*a/x*f.last).abs]
169
+ else
170
+ d * (a/x).to_f * f
171
+ end
172
+ end
173
+
174
+ # gamma_inc_Q_large_x in GSL-1.9
175
+ def q_large_x a,x,with_error=false
176
+ d = d(a,x,with_error)
177
+ d,d_err = d if with_error
178
+ sum = 1.0
179
+ term = 1.0
180
+ last = 1.0
181
+ n = 1
182
+ 1.upto(NMAX-1).each do |n|
183
+ term *= (a-n)/x
184
+ break if (term/last).abs > 1.0
185
+ break if (term/sum).abs < Float::EPSILON
186
+ sum += term
187
+ last = term
188
+ end
189
+
190
+ result = d*(a/x)*sum
191
+ error = d_err * (a/x).abs * sum if with_error
192
+
193
+ if n == NMAX
194
+ STDERR.puts("Error: n reached NMAX in q_large_x")
195
+ else
196
+ return with_error ? [result,error] : result
197
+ end
198
+ end
199
+
200
+ # Uniform asymptotic for x near a, a and x large
201
+ # gamma_inc_Q_asymp_unif
202
+ def q_asymptotic_uniform(a, x, with_error = false)
203
+ rta = Math.sqrt(a)
204
+ eps = (x-a).quo(a)
205
+
206
+ ln_term = Log::log_1plusx_minusx(eps, with_error)
207
+ ln_term, ln_term_err = ln_term if with_error
208
+
209
+ eta = (eps >= 0 ? 1 : -1) * Math.sqrt(-2*ln_term)
210
+
211
+ erfc = Math.erfc_e(eta*rta/SQRT2, with_error)
212
+ erfc, erfc_err = erfc if with_error
213
+
214
+ c0 = c1 = nil
215
+ if eps.abs < ROOT5_FLOAT_EPSILON
216
+ c0 = -1.quo(3) + eps*(1.quo(12) - eps*(23.quo(540) - eps*(353.quo(12960) - eps*589.quo(30240))))
217
+ c1 = -1.quo(540) - eps.quo(288)
218
+ else
219
+ rt_term = Math.sqrt(-2 * ln_term.quo(eps*eps))
220
+ lam = x.quo(a)
221
+ c0 = (1 - 1/rt_term)/eps
222
+ c1 = -(eta**3 * (lam*lam + 10*lam + 1) - 12*eps**3).quo(12 * eta**3 * eps**3)
223
+ end
224
+
225
+ r = Math.exp(-0.5*a*eta*eta) / (SQRT2*SQRTPI*rta) * (c0 + c1.quo(a))
226
+
227
+ result = 0.5 * erfc + r
228
+ with_error ? [result, Float::EPSILON + (r*0.5*a*eta*eta).abs + 0.5*erfc_err + 2.0*Float::EPSILON + result.abs] : result
229
+ end
230
+
231
+ # gamma_inc_F_CF
232
+ def f_continued_fraction a, x, with_error = false
233
+ hn = 1.0 # convergent
234
+ cn = 1.0 / SMALL
235
+ dn = 1.0
236
+ n = 2
237
+ 2.upto(NMAX-1).each do |n|
238
+ an = n.odd? ? 0.5*(n-1)/x : (0.5*n-a)/x
239
+ dn = 1.0 + an * dn
240
+ dn = SMALL if dn.abs < SMALL
241
+ cn = 1.0 + an / cn
242
+ cn = SMALL if cn.abs < SMALL
243
+ dn = 1.0 / dn
244
+ delta = cn * dn
245
+ hn *= delta
246
+ break if (delta-1.0).abs < Float::EPSILON
247
+ end
248
+
249
+ if n == NMAX
250
+ STDERR.puts("Error: n reached NMAX in f continued fraction")
251
+ else
252
+ with_error ? [hn,2.0*Float::EPSILON * hn.abs + Float::EPSILON*(2.0+0.5*n) * hn.abs] : hn
253
+ end
254
+ end
255
+
256
+ def q_series(a,x,with_error=false)
257
+ term1 = nil
258
+ sum = nil
259
+ term2 = nil
260
+ begin
261
+ lnx = Math.log(x)
262
+ el = EULER + lnx
263
+ c1 = -el
264
+ c2 = Math::PI * Math::PI / 12.0 - 0.5*el*el
265
+ c3 = el*(Math::PI*Math::PI/12.0 - el*el/6.0) + PG21/6.0
266
+ c4 = -0.04166666666666666667 *
267
+ (-1.758243446661483480 + lnx) *
268
+ (-0.764428657272716373 + lnx) *
269
+ ( 0.723980571623507657 + lnx) *
270
+ ( 4.107554191916823640 + lnx)
271
+ c5 = -0.0083333333333333333 *
272
+ (-2.06563396085715900 + lnx) *
273
+ (-1.28459889470864700 + lnx) *
274
+ (-0.27583535756454143 + lnx) *
275
+ ( 1.33677371336239618 + lnx) *
276
+ ( 5.17537282427561550 + lnx)
277
+ c6 = -0.0013888888888888889 *
278
+ (-2.30814336454783200 + lnx) *
279
+ (-1.65846557706987300 + lnx) *
280
+ (-0.88768082560020400 + lnx) *
281
+ ( 0.17043847751371778 + lnx) *
282
+ ( 1.92135970115863890 + lnx) *
283
+ ( 6.22578557795474900 + lnx)
284
+ c7 = -0.00019841269841269841
285
+ (-2.5078657901291800 + lnx) *
286
+ (-1.9478900888958200 + lnx) *
287
+ (-1.3194837322612730 + lnx) *
288
+ (-0.5281322700249279 + lnx) *
289
+ ( 0.5913834939078759 + lnx) *
290
+ ( 2.4876819633378140 + lnx) *
291
+ ( 7.2648160783762400 + lnx)
292
+ c8 = -0.00002480158730158730 *
293
+ (-2.677341544966400 + lnx) *
294
+ (-2.182810448271700 + lnx) *
295
+ (-1.649350342277400 + lnx) *
296
+ (-1.014099048290790 + lnx) *
297
+ (-0.191366955370652 + lnx) *
298
+ ( 0.995403817918724 + lnx) *
299
+ ( 3.041323283529310 + lnx) *
300
+ ( 8.295966556941250 + lnx) *
301
+ c9 = -2.75573192239859e-6 *
302
+ (-2.8243487670469080 + lnx) *
303
+ (-2.3798494322701120 + lnx) *
304
+ (-1.9143674728689960 + lnx) *
305
+ (-1.3814529102920370 + lnx) *
306
+ (-0.7294312810261694 + lnx) *
307
+ ( 0.1299079285269565 + lnx) *
308
+ ( 1.3873333251885240 + lnx) *
309
+ ( 3.5857258865210760 + lnx) *
310
+ ( 9.3214237073814600 + lnx) *
311
+ c10 = -2.75573192239859e-7 *
312
+ (-2.9540329644556910 + lnx) *
313
+ (-2.5491366926991850 + lnx) *
314
+ (-2.1348279229279880 + lnx) *
315
+ (-1.6741881076349450 + lnx) *
316
+ (-1.1325949616098420 + lnx) *
317
+ (-0.4590034650618494 + lnx) *
318
+ ( 0.4399352987435699 + lnx) *
319
+ ( 1.7702236517651670 + lnx) *
320
+ ( 4.1231539047474080 + lnx) *
321
+ ( 10.342627908148680 + lnx)
322
+ term1 = a*(c1+a*(c2+a*(c3+a*(c4+a*(c5+a*(c6+a*(c7+a*(c8+a*(c9+a*c10)))))))))
323
+ end
324
+
325
+ n = 1
326
+ begin
327
+ t = 1.0
328
+ sum = 1.0
329
+ 1.upto(NMAX-1).each do |n|
330
+ t *= -x/(n+1.0)
331
+ sum += (a+1.0) / (a+n+1.0) * t
332
+ break if (t/sum).abs < Float::EPSILON
333
+ end
334
+ end
335
+
336
+ if n == NMAX
337
+ STDERR.puts("Error: n reached NMAX in q_series")
338
+ else
339
+ term2 = (1.0 - term1) * a/(a+1.0) * x * sum
340
+ result = term1+term2
341
+ with_error ? [result, Float::EPSILON*term1.abs + 2.0*term2.abs + 2.0*Float::EPSILON*result.abs] : result
342
+ end
343
+ end
344
+
345
+ # gamma_inc_series
346
+ def series a,x,with_error = false
347
+ q = q_series(a,x,with_error)
348
+ g = Math.gamma(a)
349
+ STDERR.puts("Warning: Don't know error for Math.gamma. Error will be incorrect") if with_error
350
+ # When we get the error from Gamma, switch the comment on the next to lines
351
+ # with_error ? [q.first*g.first, (q.first*g.last).abs + (q.last*g.first).abs + 2.0*Float::EPSILON*(q.first*g.first).abs] : q*g
352
+ with_error ? [q.first*g, (q.first*Float::EPSILON).abs + (q.last*g.first).abs + 2.0*Float::EPSILON(q.first*g).abs] : q*g
353
+ end
354
+
355
+ # gamma_inc_a_gt_0
356
+ def a_greater_than_0 a, x, with_error = false
357
+ q = q(a,x,with_error)
358
+ q,q_err = q if with_error
359
+ g = Math.gamma(a)
360
+ STDERR.puts("Warning: Don't know error for Math.gamma. Error will be incorrect") if with_error
361
+ g_err = Float::EPSILON
362
+ result = g*q
363
+ error = (g*q_err).abs + (g_err*q).abs if with_error
364
+ with_error ? [result,error] : result
365
+ end
366
+
367
+ # gamma_inc_CF
368
+ def continued_fraction a,x, with_error=false
369
+ f = f_continued_fraction(a,x,with_error)
370
+ f,f_error = f if with_error
371
+ pre = Math.exp((a-1.0)*Math.log(x) - x)
372
+ STDERR.puts("Warning: Don't know error for Math.exp. Error will be incorrect") if with_error
373
+ pre_error = Float::EPSILON
374
+ result = f*pre
375
+ if with_error
376
+ error = (f_error*pre).abs + (f*pre_error) + (2.0+a.abs)*Float::EPSILON*result.abs
377
+ [result,error]
378
+ else
379
+ result
380
+ end
381
+ end
382
+
383
+ # Unnormalized incomplete gamma function.
384
+ # gsl_sf_gamma_inc_e
385
+ def unnormalized a,x,with_error = false
386
+ raise(ArgumentError, "x cannot be negative") if x < 0.0
387
+
388
+ if x == 0.0
389
+ result = Math.gamma(a.to_f)
390
+ STDERR.puts("Warning: Don't know error for Math.gamma. Error will be incorrect") if with_error
391
+ return with_error ? [result, Float::EPSILON] : result
392
+ elsif a == 0.0
393
+ return ExponentialIntegral.first_order(x.to_f, with_error)
394
+ elsif a > 0.0
395
+ return a_greater_than_0(a.to_f, x.to_f, with_error)
396
+ elsif x > 0.25
397
+ # continued fraction seems to fail for x too small
398
+ return continued_fraction(a.to_f, x.to_f, with_error)
399
+ elsif a.abs < 0.5
400
+ return series(a.to_f,x.to_f,with_error)
401
+ else
402
+ fa = a.floor.to_f
403
+ da = a - fa
404
+ g_da = da > 0.0 ? a_greater_than_0(da, x.to_f, with_error) : ExponentialIntegral.first_order(x.to_f, with_error)
405
+ g_da, g_da_err = g_da if with_error
406
+ alpha = da
407
+ gax = g_da
408
+
409
+ # Gamma(alpha-1,x) = 1/(alpha-1) (Gamma(a,x) - x^(alpha-1) e^-x)
410
+ begin
411
+ shift = Math.exp(-x + (alpha-1.0)*Math.log(x))
412
+ gax = (gax-shift) / (alpha-1.0)
413
+ alpha -= 1.0
414
+ end while alpha > a
415
+
416
+ result = gax
417
+ return with_error ? [result, 2.0*(1.0 + a.abs) * Float::EPSILON*gax.abs] : result
418
+ end
419
+ end
420
+
421
+ end
422
+ end
423
+ end
424
+ end