1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.numbers.gamma;
18
19
20
21
22
23
24
25 public final class LogBeta {
26
27 private static final double TEN = 10;
28
29 private static final double TWO = 2;
30
31 private static final double THOUSAND = 1000;
32
33
34 private static final double HALF_LOG_TWO_PI = 0.9189385332046727;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 private static final double[] DELTA = {
51 .833333333333333333333333333333E-01,
52 -.277777777777777777777777752282E-04,
53 .793650793650793650791732130419E-07,
54 -.595238095238095232389839236182E-09,
55 .841750841750832853294451671990E-11,
56 -.191752691751854612334149171243E-12,
57 .641025640510325475730918472625E-14,
58 -.295506514125338232839867823991E-15,
59 .179643716359402238723287696452E-16,
60 -.139228964661627791231203060395E-17,
61 .133802855014020915603275339093E-18,
62 -.154246009867966094273710216533E-19,
63 .197701992980957427278370133333E-20,
64 -.234065664793997056856992426667E-21,
65 .171348014966398575409015466667E-22
66 };
67
68
69 private LogBeta() {
70
71 }
72
73
74
75
76
77
78
79
80
81
82
83
84 private static double deltaMinusDeltaSum(final double a,
85 final double b) {
86 if (a < 0 ||
87 a > b) {
88 throw new GammaException(GammaException.OUT_OF_RANGE, a, 0, b);
89 }
90 if (b < TEN) {
91 throw new GammaException(GammaException.OUT_OF_RANGE, b, TEN, Double.POSITIVE_INFINITY);
92 }
93
94 final double h = a / b;
95 final double p = h / (1 + h);
96 final double q = 1 / (1 + h);
97 final double q2 = q * q;
98
99
100
101 final double[] s = new double[DELTA.length];
102 s[0] = 1;
103 for (int i = 1; i < s.length; i++) {
104 s[i] = 1 + (q + q2 * s[i - 1]);
105 }
106
107
108
109 final double sqrtT = 10 / b;
110 final double t = sqrtT * sqrtT;
111 double w = DELTA[DELTA.length - 1] * s[s.length - 1];
112 for (int i = DELTA.length - 2; i >= 0; i--) {
113 w = t * w + DELTA[i] * s[i];
114 }
115 return w * p / b;
116 }
117
118
119
120
121
122
123
124
125
126
127
128
129 private static double sumDeltaMinusDeltaSum(final double p,
130 final double q) {
131
132 if (p < TEN) {
133 throw new GammaException(GammaException.OUT_OF_RANGE, p, TEN, Double.POSITIVE_INFINITY);
134 }
135 if (q < TEN) {
136 throw new GammaException(GammaException.OUT_OF_RANGE, q, TEN, Double.POSITIVE_INFINITY);
137 }
138
139 final double a = Math.min(p, q);
140 final double b = Math.max(p, q);
141 final double sqrtT = 10 / a;
142 final double t = sqrtT * sqrtT;
143 double z = DELTA[DELTA.length - 1];
144 for (int i = DELTA.length - 2; i >= 0; i--) {
145 z = t * z + DELTA[i];
146 }
147 return z / a + deltaMinusDeltaSum(a, b);
148 }
149
150
151
152
153
154
155
156
157
158
159
160 public static double value(double p,
161 double q) {
162 if (Double.isNaN(p) ||
163 Double.isNaN(q) ||
164 p <= 0 ||
165 q <= 0) {
166 return Double.NaN;
167 }
168
169 final double a = Math.min(p, q);
170 final double b = Math.max(p, q);
171 if (a >= TEN) {
172 final double w = sumDeltaMinusDeltaSum(a, b);
173 final double h = a / b;
174 final double c = h / (1 + h);
175 final double u = -(a - 0.5) * Math.log(c);
176 final double v = b * Math.log1p(h);
177 if (u <= v) {
178 return (((-0.5 * Math.log(b) + HALF_LOG_TWO_PI) + w) - u) - v;
179 }
180 return (((-0.5 * Math.log(b) + HALF_LOG_TWO_PI) + w) - v) - u;
181 } else if (a > TWO) {
182 if (b > THOUSAND) {
183 final int n = (int) Math.floor(a - 1);
184 double prod = 1;
185 double ared = a;
186 for (int i = 0; i < n; i++) {
187 ared -= 1;
188 prod *= ared / (1 + ared / b);
189 }
190 return (Math.log(prod) - n * Math.log(b)) +
191 (LogGamma.value(ared) +
192 logGammaMinusLogGammaSum(ared, b));
193 }
194 double prod1 = 1;
195 double ared = a;
196 while (ared > 2) {
197 ared -= 1;
198 final double h = ared / b;
199 prod1 *= h / (1 + h);
200 }
201 if (b < TEN) {
202 double prod2 = 1;
203 double bred = b;
204 while (bred > 2) {
205 bred -= 1;
206 prod2 *= bred / (ared + bred);
207 }
208 return Math.log(prod1) +
209 Math.log(prod2) +
210 (LogGamma.value(ared) +
211 (LogGamma.value(bred) -
212 LogGammaSum.value(ared, bred)));
213 }
214 return Math.log(prod1) +
215 LogGamma.value(ared) +
216 logGammaMinusLogGammaSum(ared, b);
217 } else if (a >= 1) {
218 if (b > TWO) {
219 if (b < TEN) {
220 double prod = 1;
221 double bred = b;
222 while (bred > 2) {
223 bred -= 1;
224 prod *= bred / (a + bred);
225 }
226 return Math.log(prod) +
227 (LogGamma.value(a) +
228 (LogGamma.value(bred) -
229 LogGammaSum.value(a, bred)));
230 }
231 return LogGamma.value(a) +
232 logGammaMinusLogGammaSum(a, b);
233 }
234 return LogGamma.value(a) +
235 LogGamma.value(b) -
236 LogGammaSum.value(a, b);
237 } else {
238 if (b >= TEN) {
239 return LogGamma.value(a) +
240 logGammaMinusLogGammaSum(a, b);
241 }
242
243
244
245
246 final double beta = Gamma.value(a) * Gamma.value(b) / Gamma.value(a + b);
247 if (Double.isFinite(beta)) {
248 return Math.log(beta);
249 }
250 return LogGamma.value(a) + (LogGamma.value(b) - LogGamma.value(a + b));
251 }
252 }
253
254
255
256
257
258
259
260
261
262
263
264
265 private static double logGammaMinusLogGammaSum(double a,
266 double b) {
267 if (a < 0) {
268 throw new GammaException(GammaException.OUT_OF_RANGE, a, 0, Double.POSITIVE_INFINITY);
269 }
270 if (b < TEN) {
271 throw new GammaException(GammaException.OUT_OF_RANGE, b, TEN, Double.POSITIVE_INFINITY);
272 }
273
274
275
276
277 final double d;
278 final double w;
279 if (a <= b) {
280 d = b + (a - 0.5);
281 w = deltaMinusDeltaSum(a, b);
282 } else {
283 d = a + (b - 0.5);
284 w = deltaMinusDeltaSum(b, a);
285 }
286
287 final double u = d * Math.log1p(a / b);
288 final double v = a * (Math.log(b) - 1);
289
290 return u <= v ?
291 (w - u) - v :
292 (w - v) - u;
293 }
294 }