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
87
88
89 final double h = a / b;
90 final double p = h / (1 + h);
91 final double q = 1 / (1 + h);
92 final double q2 = q * q;
93
94
95
96 final double[] s = new double[DELTA.length];
97 s[0] = 1;
98 for (int i = 1; i < s.length; i++) {
99 s[i] = 1 + (q + q2 * s[i - 1]);
100 }
101
102
103
104 final double sqrtT = 10 / b;
105 final double t = sqrtT * sqrtT;
106 double w = DELTA[DELTA.length - 1] * s[s.length - 1];
107 for (int i = DELTA.length - 2; i >= 0; i--) {
108 w = t * w + DELTA[i] * s[i];
109 }
110 return w * p / b;
111 }
112
113
114
115
116
117
118
119
120
121
122
123
124 private static double sumDeltaMinusDeltaSum(final double p,
125 final double q) {
126
127 if (p < TEN) {
128 throw new GammaException(GammaException.OUT_OF_RANGE, p, TEN, Double.POSITIVE_INFINITY);
129 }
130 if (q < TEN) {
131 throw new GammaException(GammaException.OUT_OF_RANGE, q, TEN, Double.POSITIVE_INFINITY);
132 }
133
134 final double a = Math.min(p, q);
135 final double b = Math.max(p, q);
136 final double sqrtT = 10 / a;
137 final double t = sqrtT * sqrtT;
138 double z = DELTA[DELTA.length - 1];
139 for (int i = DELTA.length - 2; i >= 0; i--) {
140 z = t * z + DELTA[i];
141 }
142 return z / a + deltaMinusDeltaSum(a, b);
143 }
144
145
146
147
148
149
150
151
152
153
154
155 public static double value(double p,
156 double q) {
157 if (Double.isNaN(p) ||
158 Double.isNaN(q) ||
159 p <= 0 ||
160 q <= 0) {
161 return Double.NaN;
162 }
163
164 final double a = Math.min(p, q);
165 final double b = Math.max(p, q);
166 if (a >= TEN) {
167 final double w = sumDeltaMinusDeltaSum(a, b);
168 final double h = a / b;
169 final double c = h / (1 + h);
170 final double u = -(a - 0.5) * Math.log(c);
171 final double v = b * Math.log1p(h);
172 if (u <= v) {
173 return (((-0.5 * Math.log(b) + HALF_LOG_TWO_PI) + w) - u) - v;
174 }
175 return (((-0.5 * Math.log(b) + HALF_LOG_TWO_PI) + w) - v) - u;
176 } else if (a > TWO) {
177 if (b > THOUSAND) {
178 final int n = (int) Math.floor(a - 1);
179 double prod = 1;
180 double ared = a;
181 for (int i = 0; i < n; i++) {
182 ared -= 1;
183 prod *= ared / (1 + ared / b);
184 }
185 return (Math.log(prod) - n * Math.log(b)) +
186 (LogGamma.value(ared) +
187 logGammaMinusLogGammaSum(ared, b));
188 }
189 double prod1 = 1;
190 double ared = a;
191 while (ared > TWO) {
192 ared -= 1;
193 final double h = ared / b;
194 prod1 *= h / (1 + h);
195 }
196 if (b < TEN) {
197 double prod2 = 1;
198 double bred = b;
199 while (bred > TWO) {
200 bred -= 1;
201 prod2 *= bred / (ared + bred);
202 }
203 return Math.log(prod1) +
204 Math.log(prod2) +
205 (LogGamma.value(ared) +
206 (LogGamma.value(bred) -
207 LogGammaSum.value(ared, bred)));
208 }
209 return Math.log(prod1) +
210 LogGamma.value(ared) +
211 logGammaMinusLogGammaSum(ared, b);
212 } else if (a >= 1) {
213 if (b > TWO) {
214 if (b < TEN) {
215 double prod = 1;
216 double bred = b;
217 while (bred > TWO) {
218 bred -= 1;
219 prod *= bred / (a + bred);
220 }
221 return Math.log(prod) +
222 (LogGamma.value(a) +
223 (LogGamma.value(bred) -
224 LogGammaSum.value(a, bred)));
225 }
226 return LogGamma.value(a) +
227 logGammaMinusLogGammaSum(a, b);
228 }
229 return LogGamma.value(a) +
230 LogGamma.value(b) -
231 LogGammaSum.value(a, b);
232 } else {
233 if (b >= TEN) {
234 return LogGamma.value(a) +
235 logGammaMinusLogGammaSum(a, b);
236 }
237
238
239
240
241 final double beta = Gamma.value(a) * Gamma.value(b) / Gamma.value(a + b);
242 if (Double.isFinite(beta)) {
243 return Math.log(beta);
244 }
245 return LogGamma.value(a) + (LogGamma.value(b) - LogGamma.value(a + b));
246 }
247 }
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262 private static double logGammaMinusLogGammaSum(double a,
263 double b) {
264 if (a < 0) {
265 throw new GammaException(GammaException.OUT_OF_RANGE, a, 0, Double.POSITIVE_INFINITY);
266 }
267 if (b < TEN) {
268 throw new GammaException(GammaException.OUT_OF_RANGE, b, TEN, Double.POSITIVE_INFINITY);
269 }
270
271
272
273
274
275 final double d = b + (a - 0.5);
276 final double w = deltaMinusDeltaSum(a, b);
277
278 final double u = d * Math.log1p(a / b);
279 final double v = a * (Math.log(b) - 1);
280
281 return u <= v ?
282 (w - u) - v :
283 (w - v) - u;
284 }
285 }