bigdecimal 4.1.2-java → 4.1.3-java

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44ae86902ea163c37539fb69ed9510c61829bdfbcb6e1ba02f90ca8aa4c25603
4
- data.tar.gz: c7aa4d33cf57cf5a78f1dd23ffeb3fab30ea423189c77873904c5e059922708b
3
+ metadata.gz: e2513e3ca1694eeafb719fc68e0b3fbddc8d358db7308e08d1877e4e50183eb5
4
+ data.tar.gz: 332693659e236e971b66264b8710690b2d80f9b8e517f70a8cf6843d471c5a0b
5
5
  SHA512:
6
- metadata.gz: '0183b35444f3360f1a04ca9895efb7fb8d943fff443fe8f08381125b5fa8d3b3270d4b1099d3545c30899ed4cee1dec6f37377cf8f6a9f0dbd5f559b35b4fb51'
7
- data.tar.gz: f1b848b5219995f13ac18e90cd4cdc8f0a780d4b536984d5c55d41cdefc7eb19571f0e2ac06afda3b1a82fecb1c00f99c11ea50742953ef59f156febfd914840
6
+ metadata.gz: aa23abeaadf3797702ce3887b657871d669cf0cbd66d3c38cd7ca6fcbcd9b394ac2e6d8f1ddf887e619f3f627e78f05168dc9958d2860de5fc44edecd0d427c3
7
+ data.tar.gz: 8360c5dfa48f212fa033ad69f7eccfe5b321d8b9c7736980ce4140952d39f04b03069d441c5f59c77dec0250ed2e786b32c1a973712c4e2e9ff6f70a624bdae7
data/bigdecimal.gemspec CHANGED
@@ -29,6 +29,8 @@ Gem::Specification.new do |s|
29
29
  lib/bigdecimal/jacobian.rb
30
30
  lib/bigdecimal/ludcmp.rb
31
31
  lib/bigdecimal/math.rb
32
+ lib/bigdecimal/math/erf.rb
33
+ lib/bigdecimal/math/gamma.rb
32
34
  lib/bigdecimal/newton.rb
33
35
  lib/bigdecimal/util.rb
34
36
  sample/linear.rb
@@ -0,0 +1,291 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BigMath
4
+ # Bit-burst implementation of BigMath.erf and BigMath.erfc.
5
+ #
6
+ # Both functions share the same incremental update: given erf(x0+...+xk) (or erfc),
7
+ # extend to erf(x0+...+xk+x_{k+1}) by adding (or, for erfc, subtracting) the Taylor
8
+ # expansion of the difference function
9
+ # g(t) := (erf(t + a) - erf(a)) * exp(a**2) * sqrt(pi) / 2 with a = x0+...+xk
10
+ # which satisfies the homogeneous ODE g''(t) + 2*(t+a)*g'(t) = 0.
11
+ # Each step uses binary splitting on the 3-term recurrence of g's Taylor coefficients;
12
+ # split widths x1, x2, ... double in digits, giving quasi-linear total cost.
13
+ #
14
+ # Only the bit-burst seed differs between the two:
15
+ # erf : seed = erf(x0) via Taylor expansion at 0
16
+ # erfc : seed = erfc(x0) via asymptotic expansion (requires x0 large enough;
17
+ # returns nil if asymptotic cannot reach the requested precision, in which
18
+ # case erfc(x) is recovered from 1 - erf(x) with extra digits to absorb
19
+ # cancellation)
20
+ #
21
+ # Edge cases (after symmetry erf(-x) = -erf(x)):
22
+ # x == 0 : erf = 0
23
+ # x > 5e9 : erf = 1, erfc underflows
24
+ # x < 0.5 (erfc only) : compute via 1 - erf to avoid unnecessary work
25
+ module Erf # :nodoc:
26
+
27
+ # Calculates erf with given precision.
28
+ def self.erf(x, prec)
29
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :erf)
30
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erf)
31
+ return BigDecimal::Internal.nan_computation_result if x.nan?
32
+ return BigDecimal(x.infinite?) if x.infinite?
33
+ return BigDecimal(0) if x == 0
34
+ return -erf(-x, prec) if x < 0
35
+ return BigDecimal(1) if x > 5000000000 # erf(5000000000) > 1 - 1e-10000000000000000000
36
+ if x > 8
37
+ xf = x.to_f
38
+ log10_erfc = -xf ** 2 / Math.log(10) - Math.log10(xf * Math::PI ** 0.5)
39
+ erfc_prec = [prec + log10_erfc.ceil, 1].max
40
+ erfc = erfc_bit_burst(x, erfc_prec + BigDecimal::Internal::EXTRA_PREC)
41
+ return BigDecimal(1).sub(erfc, prec) if erfc
42
+ end
43
+
44
+ erf_bit_burst(x, prec + BigDecimal::Internal::EXTRA_PREC).mult(1, prec)
45
+ end
46
+
47
+ # Calculates erfc with given precision.
48
+ def self.erfc(x, prec)
49
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :erfc)
50
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erfc)
51
+ return BigDecimal::Internal.nan_computation_result if x.nan?
52
+ return BigDecimal(1 - x.infinite?) if x.infinite?
53
+ return BigDecimal(1).sub(erf(x, prec + BigDecimal::Internal::EXTRA_PREC), prec) if x < 0.5
54
+ return BigDecimal::Internal.underflow_computation_result if x > 5000000000 # erfc(5000000000) < 1e-10000000000000000000 (underflow)
55
+
56
+ if x > 8
57
+ y = erfc_bit_burst(x, prec + BigDecimal::Internal::EXTRA_PREC)
58
+ return y.mult(1, prec) if y
59
+ end
60
+
61
+ # erfc(x) = 1 - erf(x) < exp(-x**2)/x/sqrt(pi)
62
+ # Precision of erf(x) needs about log10(exp(-x**2)/x/sqrt(pi)) extra digits
63
+ log10 = 2.302585092994046
64
+ xf = x.to_f
65
+ high_prec = prec + BigDecimal::Internal::EXTRA_PREC + ((xf**2 + Math.log(xf) + Math.log(Math::PI)/2) / log10).ceil
66
+ BigDecimal(1).sub(erf_bit_burst(x, high_prec), prec)
67
+ end
68
+
69
+ # Matrix multiplication. m1 and m2 are size*size length array that represents size*size matrix
70
+ def self.matrix_mult(m1, m2, size, prec)
71
+ (size * size).times.map do |i|
72
+ size.times.map do |k|
73
+ m1[i / size * size + k].mult(m2[size * k + i % size], prec)
74
+ end.reduce {|a, b| a.add(b, prec) }
75
+ end
76
+ end
77
+
78
+ # Returns (erf(x + a) - erf(a)) * exp(a**2) * sqrt(pi) / 2 calculated with binary splitting method.
79
+ def self.erf_binary_splitting_diff(x, a, prec)
80
+ # Let f(x) = (erf(x + a) - erf(a)) * exp(a**2) * sqrt(pi) / 2
81
+ # f(x) satisfies the following differential equation:
82
+ # 2*(x+a)*f'(x) + f''(x) = 0
83
+ # We can derive the following recurrence for the Taylor coefficients of f:
84
+ # f(x) = x * (c0 + c1*x + c2*x**2 + c3*x**3 + ...)
85
+ # c(0) = 1
86
+ # c(1) = -a
87
+ # c(i) = -2 * (a * c(i - 1) + c(i - 2) * (i - 1) / i) / (i + 1)
88
+
89
+ # Estimate required number of terms by calculating c(i) with low precision
90
+ low_prec = 10
91
+ a_low = a.mult(1, low_prec)
92
+ x_low = x.mult(1, low_prec)
93
+ coefs = [BigDecimal(1), -a_low]
94
+ xn = BigDecimal(1)
95
+ threshold = BigDecimal(1)._decimal_shift(-prec)
96
+ steps = (2..).find do |n|
97
+ prevprev, prev = coefs
98
+ xn = xn.mult(x_low, low_prec)
99
+ coefs = prev, (a_low * prev + (prevprev * (n - 1)).div(n, low_prec)).mult(-2, low_prec).div(n + 1, low_prec)
100
+ coefs[0].mult(xn, low_prec).abs < threshold && coefs[1].mult(xn * x_low, low_prec).abs < threshold
101
+ end
102
+
103
+ # Let M(i) be a 2x2 matrix that generates the next coefficients vector (c(i-1), c(i))
104
+ # from the previous two coefficients (c(i-2), c(i-1)).
105
+ # M(i) = | 0, 1 |
106
+ # | -2*(i-1)/i/(i+1), -2*a/(i+1) |
107
+ #
108
+ # Then, we can calculate (c(steps-1), c(steps)) as M(steps)*M(steps-1)*...*M(2)*Vector(c0, c1).
109
+ #
110
+ # Calculate a matrix that represents the sum of the Taylor series:
111
+ # SumMatrix = ((((...+I)x*M4+I)*x*M3+I)*M2*x+I)
112
+ # Actual sum can be calculated as:
113
+ # SumMatrix * Vector(c0, c1) = Vector(c0+c1*x+c2*x**2+c3*x**3+..., _)
114
+ # In this binary splitting method, adjacent two operations are combined into one repeatedly.
115
+ # ((...) * x * A + B) / C is the form of each operation. A and B are 2x2 matrices, C is a scalar.
116
+
117
+ zero = BigDecimal(0)
118
+ operations = (2..steps + 2).map do |i|
119
+ d = BigDecimal(i * (i + 1))
120
+ [[zero, d, BigDecimal(-2 * (i - 1)), a * (-2 * i)], [d, zero, zero, d], d]
121
+ end
122
+
123
+ while operations.size > 1
124
+ xpow = xpow ? xpow.mult(xpow, prec) : x.mult(1, prec)
125
+ operations = operations.each_slice(2).map do |op1, op2|
126
+ # Combine two operations into one:
127
+ # (((Remaining * x * A2 + B2) / C2) * x * A1 + B1) / C1
128
+ # ((Remaining * (x*x) * (A2*A1) + (x*B2*A1+B1*C2)) / (C1*C2)
129
+ # Therefore, combined operation can be represented as:
130
+ # Anext = A2 * A1
131
+ # Bnext = x * B2 * A1 + B1 * C2
132
+ # Cnext = C1 * C2
133
+ # xnext = x * x
134
+ a1, b1, c1 = op1
135
+ a2, b2, c2 = op2 || [[zero] * 4, [zero] * 4, BigDecimal(1)]
136
+ [
137
+ matrix_mult(a2, a1, 2, prec),
138
+ array_weighted_sum(matrix_mult(b2, a1, 2, prec), xpow, b1, c2, prec),
139
+ c1.mult(c2, prec),
140
+ ]
141
+ end
142
+ end
143
+ _, sum_matrix, denominator = operations.first
144
+ sum = (sum_matrix[0] - a * sum_matrix[1]).div(denominator, prec)
145
+ x.mult(sum, prec)
146
+ end
147
+
148
+ # Calculates erfc(x) using bit-burst algorithm.
149
+ # Returns nil if the asymptotic expansion does not reach the requested precision.
150
+ def self.erfc_bit_burst(x, prec)
151
+ # By bounding the relative error via |d(erfc)/erfc| <= 2*x*|dx| (erfc(x) decays as exp(-x**2)/x),
152
+ # truncate x to the minimum digits sufficient for prec-digit accuracy of the result.
153
+ x = x.mult(1, prec + Math.log10(2 * x.to_f**2).ceil)
154
+ erf_erfc_bit_burst(x, prec, start_digits: 40, mode: :erfc)
155
+ end
156
+
157
+ # Calculates erf(x) using bit-burst algorithm.
158
+ def self.erf_bit_burst(x, prec)
159
+ # By bounding the error via erf'(x) = (2/sqrt(pi)) * exp(-x**2),
160
+ # truncate x to the minimum digits sufficient for prec-digit accuracy of the result.
161
+ x = x.mult(1, [(prec - x.floor**2 / Math.log(10) + Math.log10(x.ceil)).ceil, 10].max)
162
+ erf_erfc_bit_burst(x, prec, start_digits: 8, mode: :erf)
163
+ end
164
+
165
+ # Calculates erf or erfc using bit-burst algorithm.
166
+ # Returns nil if erfc mode cannot reach the requested precision.
167
+ def self.erf_erfc_bit_burst(x, prec, start_digits:, mode:)
168
+ digits = [-x.exponent * 2, start_digits].max
169
+ partial = x.truncate(digits)
170
+ case mode
171
+ when :erf
172
+ f = erf_exp2_binary_splitting(partial, prec)
173
+ when :erfc
174
+ f = erfc_exp2_asymptotic_binary_splitting(partial, prec)
175
+ return unless f
176
+ end
177
+
178
+ exp_scale = BigMath.exp(-partial * partial, prec)
179
+ f = f.mult(exp_scale, prec)
180
+
181
+ calculated_x = partial
182
+ x -= partial
183
+
184
+ until x.zero?
185
+ digits *= 2
186
+ partial = x.truncate(digits)
187
+ next if partial.zero?
188
+
189
+ diff_prec = [prec - f.exponent + exp_scale.exponent + partial.exponent, 1].max
190
+ diff = erf_binary_splitting_diff(partial, calculated_x, diff_prec)
191
+ case mode
192
+ when :erf
193
+ f = f.add(diff.mult(exp_scale, prec), prec)
194
+ when :erfc
195
+ f = f.sub(diff.mult(exp_scale, prec), prec)
196
+ end
197
+
198
+ calculated_x += partial
199
+ x -= partial
200
+ exp_scale = exp_scale.mult(BigMath.exp(partial * (partial - 2 * calculated_x), diff_prec), diff_prec) unless x.zero?
201
+ end
202
+ f.mult(BigDecimal(2).div(BigMath::PI(prec).sqrt(prec), prec), prec)
203
+ end
204
+
205
+ # Matrix/Vector weighted sum
206
+ def self.array_weighted_sum(m1, w1, m2, w2, prec)
207
+ m1.zip(m2).map {|v1, v2| (v1 * w1).add(v2 * w2, prec) }
208
+ end
209
+
210
+ # Calculates Taylor expansion of erf(x)*exp(x**2)*sqrt(pi)/2 with binary splitting method.
211
+ def self.erf_exp2_binary_splitting(x, prec)
212
+ # Let f(x) = erf(x)*exp(x**2)*sqrt(pi)/2
213
+ # = c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...
214
+ # f(x) is designed to make all coefficients positive so that we don't need to consider cancellation error.
215
+ #
216
+ # f(x) satisfies the following differential equation:
217
+ # f'(x) = 1 + 2 * x * f(x)
218
+ # f'(x) = c1 + 2*c2*x + 3*c3*x**2 + 4*c4*x**3 + 5*c5*x**4 + ...
219
+ # = 1+2*x*(c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...)
220
+ # therefore,
221
+ # c0 = 0
222
+ # c1 = 1
223
+ # c2 = 2 * (c0 + c1) / 2
224
+ # c3 = 2 * (c1 + c2) / 3
225
+ # c4 = 2 * (c2 + c3) / 4
226
+
227
+ # Find the smallest n where the n-th Taylor term |c_n * x^n| falls below the precision
228
+ # threshold, using a Stirling-based upper bound on |c_n|.
229
+ log10f = Math.log(10)
230
+ cexponent = Math.log10(Math.sqrt(2)) + BigDecimal::Internal.float_log(x.abs) / log10f
231
+
232
+ x_to_f = x < 1e-300 ? 1e-300 : x.to_f # x.to_f may underflow when x is very small (e.g. 1e-400)
233
+ steps = (2..).bsearch do |n|
234
+ x_to_f ** 2 < n && n * cexponent + Math.lgamma(n / 2)[0] / log10f + n * Math.log10(2) - Math.lgamma(n - 1)[0] / log10f < -prec + x_to_f**2 / log10f
235
+ end
236
+
237
+ denominators = (steps / 2).times.map {|i| 2 * i + 3 }
238
+ x.mult(1 + BigDecimal::Internal.taylor_sum_binary_splitting(2 * x * x, denominators, prec), prec)
239
+ end
240
+
241
+ # Calculates asymptotic expansion of erfc(x)*exp(x**2)*sqrt(pi)/2 with binary splitting method
242
+ def self.erfc_exp2_asymptotic_binary_splitting(x, prec)
243
+ # Let f(x) = erfc(x)*sqrt(pi)*exp(x**2)/2
244
+ # f(x) satisfies the following differential equation:
245
+ # 2*x*f(x) = f'(x) + 1
246
+ # From the above equation, we can derive the following asymptotic expansion:
247
+ # f(x) = (0..kmax).sum { (-1)**k * (2*k)! / 4**k / k! / x**(2*k) } / x / 2
248
+
249
+ # This asymptotic expansion does not converge.
250
+ # But if there is a k that satisfies (2*k)! / 4**k / k! / x**(2*k) < 10**(-prec),
251
+ # It is enough to calculate erfc within the given precision.
252
+ # Using Stirling's approximation, we can simplify this condition to:
253
+ # log(2)/2 + k*log(k) - k - 2*k*log(x) < -prec*log(10)
254
+ # and the left side is minimized when k = x**2.
255
+ xf = x.to_f
256
+ kmax = (1..(xf ** 2).floor).bsearch do |k|
257
+ Math.log(2) / 2 + k * Math.log(k) - k - 2 * k * Math.log(xf) < -prec * Math.log(10)
258
+ end
259
+ return unless kmax
260
+
261
+ # Convert asymptotic expansion to nested form:
262
+ # 1 + a/x + a*b/x/x + a*b*c/x/x/x + a*b*c/x/x/x*rest
263
+ # = 1 + (a/x) * (1 + (b/x) * (1 + (c/x) * (1 + rest)))
264
+ #
265
+ # And calculate it with binary splitting:
266
+ # (a1/d + b1/d * (a2/d + b2/d * (rest)))
267
+ # = ((a1*d+b1*a2)/(d*d) + b1*b2/(d*denominator) * (rest)))
268
+ denominator = x.mult(x, prec).mult(2, prec)
269
+ fractions = (1..kmax).map do |k|
270
+ [denominator, BigDecimal(1 - 2 * k)]
271
+ end
272
+ while fractions.size > 1
273
+ fractions = fractions.each_slice(2).map do |fraction1, fraction2|
274
+ a1, b1 = fraction1
275
+ a2, b2 = fraction2 || [BigDecimal(0), denominator]
276
+ [
277
+ a1.mult(denominator, prec).add(b1.mult(a2, prec), prec),
278
+ b1.mult(b2, prec),
279
+ ]
280
+ end
281
+ denominator = denominator.mult(denominator, prec)
282
+ end
283
+ # Plug rest = 1 into the merged form: the innermost "(1 + rest)" of the nested expansion
284
+ # evaluates to 1 at truncation (rest = 0).
285
+ sum = fractions[0][0].add(fractions[0][1], prec).div(denominator, prec)
286
+ sum.div(x, prec) / 2
287
+ end
288
+ end
289
+
290
+ private_constant :Erf
291
+ end
@@ -0,0 +1,513 @@
1
+ # frozen_string_literal: true
2
+ require 'bigdecimal/math'
3
+
4
+ module BigMath
5
+
6
+ # Calculates gamma/lgamma
7
+ # Algorithm overview:
8
+ #
9
+ # Lagrange interpolation of f(x) = b**x / x! at integer nodes x_i = b-l, ..., b+l.
10
+ # BSM(Binary Splitting Method) version for small digit numbers, O(PREC*log(PREC)^3)
11
+ # BSGS(Baby-Step Giant-Step) version for full digit numbers, O(PREC^2*log(log(PREC)))
12
+ # Both orders of magnitude faster than Spouge's approximation which is O(PREC^2*log(PREC))
13
+ # (Complexities assume quasi-linear multiplication, counting large-by-small products
14
+ # as (n/m) * M(m) = n * log(m) bit ops. BigDecimal multiplies the small coefficients
15
+ # by schoolbook instead: an extra log factor asymptotically, but faster at any feasible PREC.)
16
+ # Requires fast factorial of an integer near x (see Factorial Doubling below).
17
+ #
18
+ # Factorial Doubling for fast calculation of large factorials:
19
+ # Using Legendre duplication formula, we can calculate factorial(2n) from factorial(n) and factorial(n + 0.5).
20
+ # Calculating factorial(n + 0.5) is done by the BSM version of Lagrange interpolation in quasi-linear time.
21
+ # This will drastically reduce the cost of calculating large factorials.
22
+ # O(PREC*log(PREC)^3*log(factorial_argument))
23
+ #
24
+ # Stirling's approximation with Bernoulli numbers
25
+ # Only used when x is extremely large.
26
+
27
+ module Gamma # :nodoc:
28
+
29
+ # Calculates gamma function with given precision.
30
+ def self.gamma(x, prec)
31
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :gamma)
32
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :gamma)
33
+ prec2 = prec + BigDecimal::Internal::EXTRA_PREC
34
+
35
+ if x < 0.5
36
+ raise Math::DomainError, 'Numerical argument is out of domain - gamma' if x.frac.zero?
37
+
38
+ # Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z)
39
+ pi = BigMath::PI(prec2)
40
+ sin = sinpix(x, pi, prec2)
41
+ pi.div(gamma(1 - x, prec2).mult(sin, prec2), prec)
42
+ else
43
+ # Digits of x beyond the working precision cannot affect the result.
44
+ # Rounding must happen before the integer test: an x indistinguishable from
45
+ # an integer must take the exact integer path, because gamma_lagrange
46
+ # requires a non-integer x (an integer x makes a node distance exactly zero).
47
+ x = x.mult(1, prec2 + x.exponent + 10)
48
+ if x.frac.zero?
49
+ integer_factorial(x.to_i - 1, prec2).mult(1, prec)
50
+ else
51
+ base, large_factorial_arg, small_factorial_arg, exp2 = gamma_lagrange(x, prec2)
52
+ ans = base.mult(integer_factorial(small_factorial_arg, prec2), prec2)
53
+ ans = ans.mult(BigDecimal(2).power(exp2, prec2), prec2) unless exp2.zero?
54
+ ans.mult(integer_factorial(large_factorial_arg, prec2), prec)
55
+ end
56
+ end
57
+ end
58
+
59
+ # Calculates log gamma and its sign with given precision.
60
+ def self.lgamma(x, prec)
61
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :lgamma)
62
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :lgamma)
63
+ prec2 = prec + BigDecimal::Internal::EXTRA_PREC
64
+ if x < 0.5
65
+ return [BigDecimal::INFINITY, 1] if x.frac.zero?
66
+
67
+ loop do
68
+ # Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z)
69
+ pi = BigMath::PI(prec2)
70
+ sin = sinpix(x, pi, prec2)
71
+ log_gamma = BigMath.log(pi, prec2).sub(lgamma(1 - x, prec2).first + BigMath.log(sin.abs, prec2), prec)
72
+ return [log_gamma, sin > 0 ? 1 : -1] if log_gamma != 0 && prec2 + log_gamma.exponent > prec + BigDecimal::Internal::EXTRA_PREC
73
+
74
+ # Retry with higher precision if loss of significance is too large
75
+ prec2 = prec2 * 3 / 2
76
+ end
77
+ else
78
+ # if x is close to 1 or 2, increase precision to reduce loss of significance
79
+ diff1_exponent = x < 3 ? (x - 1).exponent : 0
80
+ diff2_exponent = x < 3 ? (x - 2).exponent : 0
81
+ extremely_near_one = diff1_exponent < -prec2
82
+ extremely_near_two = diff2_exponent < -prec2
83
+
84
+ if extremely_near_one || extremely_near_two
85
+ # If x is extremely close to base = 1 or 2, linear interpolation is accurate enough.
86
+ # Taylor expansion at x = base is: (x - base) * digamma(base) + (x - base) ** 2 * trigamma(base) / 2 + ...
87
+ # And we can ignore (x - base) ** 2 and higher order terms.
88
+ base = extremely_near_one ? 1 : 2
89
+ d = BigDecimal(1)._decimal_shift(1 - prec2)
90
+ log_gamma_d, sign = lgamma(base + d, prec2)
91
+ return [log_gamma_d.mult(x - base, prec2).div(d, prec), sign]
92
+ end
93
+
94
+ prec2 += [-diff1_exponent, -diff2_exponent, 0].max
95
+
96
+ # Same rounding as in Gamma.gamma. Must come after the near 1 and 2 handling
97
+ # above, which needs the exact distance from x to 1 or 2.
98
+ x = x.mult(1, prec2 + x.exponent + 10)
99
+
100
+ # When x is extremely large, the cost of Bernoulli number generation for Stirling's
101
+ # asymptotic expansion is smaller than the cost of multiple steps of doubling method.
102
+ # The condition is based on heuristic cost estimation and empirical tuning.
103
+ if x > prec2 && x.exponent > Integer.sqrt(prec2) / 6
104
+ [lgamma_stirling(x, prec2).mult(1, prec), 1]
105
+ elsif x.frac.zero?
106
+ [integer_factorial_log(x.to_i - 1, prec2).mult(1, prec), 1]
107
+ else
108
+ base, large_factorial_arg, small_factorial_arg, exp2 = gamma_lagrange(x, prec2)
109
+ lgamma = BigMath.log(base, prec2)
110
+ lgamma = lgamma.add(BigMath.log(2, prec2) * exp2, prec2) unless exp2.zero?
111
+ lgamma = lgamma.add(integer_factorial_log(small_factorial_arg, prec2), prec2)
112
+ lgamma = lgamma.add(integer_factorial_log(large_factorial_arg, prec2), prec)
113
+ [lgamma, 1]
114
+ end
115
+ end
116
+ end
117
+
118
+ # Calculates prod { x - k } for k in ks and the coefficients of the expanded polynomial.
119
+ # xn is an array of precalculated powers of x: [1, x, x**2, x**3, ...]
120
+ def self.x_minus_k_prod_coef(ks, xn, prec)
121
+ coef = [1]
122
+ ks.each do |k|
123
+ coef_next = [0] * (coef.size + 1)
124
+ coef.each_with_index do |c, i|
125
+ coef_next[i] -= k * c
126
+ coef_next[i + 1] += c
127
+ end
128
+ coef = coef_next
129
+ end
130
+
131
+ prod = coef.each_with_index.map do |c, i|
132
+ xn[i].mult(c, prec)
133
+ end.reduce do |sum, value|
134
+ sum.add(value, prec)
135
+ end
136
+ [prod, coef]
137
+ end
138
+
139
+ # Bit length to keep in bit-dropping integer products and fraction merges.
140
+ # Only about prec * log2(10) bits are needed. 10 / 3 slightly exceeds log2(10),
141
+ # and 64 extra bits absorb the ~1 bit lost per truncation over the tree depth.
142
+ def self.drop_cap_bits(prec)
143
+ (prec * 10 + 192) / 3
144
+ end
145
+
146
+ # Calculate numbers.reduce(:*) of integers by Binary Splitting Method.
147
+ # Returns [mantissa, exp2] representing mantissa * 2**exp2.
148
+ # With cap, lower bits of intermediate products are dropped to keep each
149
+ # multiplication cost bounded by the target precision. Without cap, the product is exact.
150
+ def self.int_bsm_prod(numbers, cap = nil)
151
+ numbers = numbers.to_a
152
+ exp2 = 0
153
+ while numbers.size > 1
154
+ numbers = numbers.each_slice(2).map do |a, b|
155
+ next a unless b
156
+ v = a * b
157
+ if cap && (s = v.bit_length - cap) > 0
158
+ exp2 += s
159
+ v >>= s
160
+ end
161
+ v
162
+ end
163
+ end
164
+ [numbers.first || 1, exp2]
165
+ end
166
+
167
+ # Calculate factorial for integer n
168
+ def self.integer_factorial(n, prec)
169
+ power_part, exp2, exp_sqrtpi = integer_factorial_parameter(n, prec)
170
+ ans = BigDecimal(2).power(exp2, prec)
171
+ power_part.each_with_index do |base, index|
172
+ ans = ans.mult(base.power(1 << index, prec), prec)
173
+ end
174
+ if exp_sqrtpi != 0
175
+ pi = BigMath::PI(doubling_level_prec(prec, power_part.size - 1))
176
+ # exp_sqrtpi is 2**k - 1 (odd): the doubling recursion squares the child's
177
+ # sqrt(pi) exponent and adds one, so only the last level's sqrt survives
178
+ pipow = pi.power(exp_sqrtpi / 2, prec).mult(pi.sqrt(prec), prec)
179
+ ans = ans.div(pipow, prec)
180
+ end
181
+ ans
182
+ end
183
+
184
+ # Precision for values that integer_factorial raises to the power 2**index.
185
+ # The power multiplies their relative error by 2**index, so they need
186
+ # index * log10(2) more digits than the result.
187
+ def self.doubling_level_prec(prec, index)
188
+ prec + (index * Math.log10(2)).ceil + 1
189
+ end
190
+
191
+ # Calculate log factorial for integer n
192
+ def self.integer_factorial_log(n, prec)
193
+ power_part, exp2, exp_sqrtpi = integer_factorial_parameter(n, prec)
194
+ ans = exp2.zero? ? BigDecimal(0) : BigMath.log(2, prec) * exp2
195
+ power_part.each_with_index do |base, index|
196
+ ans = ans.add(BigMath.log(base, prec) * (1 << index), prec)
197
+ end
198
+ if exp_sqrtpi != 0
199
+ pi = BigMath::PI(prec)
200
+ ans = ans.sub(BigMath.log(pi, prec) * (BigDecimal(exp_sqrtpi) / 2), prec)
201
+ end
202
+ ans
203
+ end
204
+
205
+ # Calculates parameters for integer factorial calculation.
206
+ # Returns [base_power_part, exp2, exp_sqrtpi] that can produce factorial(n) as:
207
+ # factorial(n) = prod { base_power_part[i]**(1 << i) } * 2**exp2 / sqrt(pi)**exp_sqrtpi
208
+ # These parameters are used to avoid overflow when calculating log factorial and lgamma for large n.
209
+ def self.integer_factorial_parameter(n, prec)
210
+ base_power_part, factorial_power_part, exp2, exp_sqrtpi = integer_factorial_recursive(n, prec)
211
+ fact_x = 1
212
+ fact_y = BigDecimal(1)
213
+ # factorial_power_part is non-decreasing (deeper recursion levels have smaller b,
214
+ # and gamma_lagrange_l grows as b shrinks), so fact_y can be extended incrementally.
215
+ # fact_y is carried over to every later index, so it needs the precision
216
+ # of the deepest level from the start.
217
+ level_prec = doubling_level_prec(prec, factorial_power_part.size - 1)
218
+ factorial_power_part.each_with_index do |factorial_arg, index|
219
+ # Exact product (no bit drop): these ranges total only O(prec * log(prec)) digits,
220
+ # and a dropped 2**s here would be raised to 2**index, exceeding the representable
221
+ # exponent range while base_power_part[index] underflows by the same amount.
222
+ mantissa, = int_bsm_prod(fact_x + 1..factorial_arg)
223
+ fact_y = fact_y.mult(mantissa, level_prec)
224
+ fact_x = factorial_arg
225
+ base_power_part[index] = base_power_part[index].mult(fact_y, level_prec)
226
+ end
227
+ [base_power_part, exp2, exp_sqrtpi]
228
+ end
229
+
230
+ # Returns [base_power_part, factorial_power_part, exp2, exp_sqrtpi] that can produce factorial(n) as:
231
+ # factorial(n) = prod { base_power_part[i]**(1 << i) } * prod { factorial(factorial_power_part[i])**(1 << i) } * 2**exp2 / sqrt(pi)**(exp_sqrtpi)
232
+ # If n is large, this method recursively calculates factorial for smaller n by Legendre duplication formula.
233
+ # index is the recursion depth, which is also the position in the returned arrays.
234
+ def self.integer_factorial_recursive(n, prec, index = 0)
235
+ level_prec = doubling_level_prec(prec, index)
236
+ if n < 4 * prec
237
+ mantissa, exp2 = int_bsm_prod(1..n, drop_cap_bits(level_prec))
238
+ return [[BigDecimal(mantissa)], [], exp2, 0]
239
+ end
240
+
241
+ # Use Legendre duplication formula to reduce factorial(n) to half-size factorials:
242
+ # factorial(n) = factorial(n/2.0) * factorial((n-1)/2.0) * 2**n / sqrt(pi)
243
+ # gamma_lagrange((n + 1) / 2 + 0.5, prec) computes the half-integer factorial
244
+ # (whichever of the two factors above is a half-integer).
245
+ half_arg = BigDecimal((n + 1) / 2) + BigDecimal('0.5')
246
+ base, large_factorial_arg, small_factorial_arg, lagrange_exp2 = gamma_lagrange(half_arg, level_prec)
247
+
248
+ range_mantissa, = int_bsm_prod(large_factorial_arg + 1..n / 2) # exact: total size is O(prec) digits
249
+ base = base.mult(range_mantissa, level_prec)
250
+ base_power_part, factorial_power_part, exp2, exp_sqrtpi = integer_factorial_recursive(large_factorial_arg, prec, index + 1)
251
+ [
252
+ [base] + base_power_part,
253
+ [small_factorial_arg] + factorial_power_part,
254
+ exp2 * 2 + n + lagrange_exp2,
255
+ exp_sqrtpi * 2 + 1
256
+ ]
257
+ end
258
+
259
+ # Estimate the required number of interpolation points `l` to achieve `prec` digits.
260
+ #
261
+ # Assuming the nodes stay strictly positive, the function b^x/x! approximates a
262
+ # Gaussian curve e^(-y^2 / 2b) around its peak (x=b).
263
+ # The Taylor coefficient of degree 2l is roughly c_2l = 1 / (l! * (2b)^l).
264
+ # Multiplying this by the distance product of 2l+1 nodes (approx (l/e)^(2l)),
265
+ # the overall truncation error E is bounded by: E ~ (l / 2eb)^l.
266
+ #
267
+ # Setting E <= 10^-prec gives the implicit equation:
268
+ # l * log10(2 * e * b / l) = prec => l = prec / log10(2 * e * b / l)
269
+ def self.gamma_lagrange_l(b, prec)
270
+ # Initial guess of l. When b >= 2 * prec - 1 (guaranteed by the shift in gamma_lagrange),
271
+ # this is safely larger than the actual l.
272
+ l = prec
273
+
274
+ # Solves the implicit equation via fixed-point iteration.
275
+ # Due to the slow growth of the logarithm, 2 iterations are practically sufficient.
276
+ 2.times { l = prec / Math.log10(2 * Math::E * b / l) }
277
+ l.ceil + 10 # Adds safety margin
278
+ end
279
+
280
+ # Calculate approximate gamma by Lagrange interpolation of f(x) = b**x / x!
281
+ # Nodes are placed at x_i = b-l, b-l+1, ..., b+l.
282
+ # b: x.round, l: number of nodes on one side (total nodes = 2*l+1)
283
+ #
284
+ # Mathematically, we use the barycentric interpolation form:
285
+ # f(x) \approx \omega(x) \sum_{i} \frac{w_i f(x_i)}{x - x_i}
286
+ # Therefore, \Gamma(x+1) = x! = b**x / f(x)
287
+ #
288
+ # Time complexity:
289
+ # - O(PREC*log(PREC)^3) for small-digit x (Binary Splitting)
290
+ # - O(PREC^2*log(log(PREC))) for full-digit x (Baby-step Giant-step).
291
+ # Measured time grows like PREC^2.
292
+ #
293
+ # Returns [base, large_factorial_arg, small_factorial_arg, exp2] that can produce gamma(x) as:
294
+ # gamma(x) = base * 2**exp2 * factorial(large_factorial_arg) * factorial(small_factorial_arg)
295
+ def self.gamma_lagrange(x, prec)
296
+ # Shift x to establish a safe center (b) for the barycentric interpolation.
297
+ #
298
+ # We must keep all interpolation nodes strictly positive (b - l > 0). Approaching
299
+ # x = 0 breaks the Gaussian approximation used to estimate `l` and provides no
300
+ # useful information for the interpolation.
301
+ #
302
+ # While b =~ 1.36 * prec is the strict theoretical minimum to stay positive, we
303
+ # heuristically use b = 2 * prec. This moves the nodes safely away from x = 0,
304
+ # stabilizes the curve, and empirically yields the optimal total computation cost.
305
+ # (See `gamma_lagrange_l` for the mathematical derivation of the approximation).
306
+ shift = x < 2 * prec ? 2 * prec - x.floor : 0
307
+ x += shift
308
+
309
+ x = BigDecimal(x) - 1
310
+ b = x.round
311
+ l = gamma_lagrange_l(b, prec)
312
+ exp2 = 0
313
+
314
+ # --- Reference: Naive interpolation logic ---
315
+ # The two branches below optimize this calculation for the full-digit and small-digit x cases.
316
+ # sum = BigDecimal(0)
317
+ # prod = [*(b - l..b + l), *(0...shift)].map {|i| x - i }.reduce { _1.mult(_2, prec) }
318
+ # c = BigDecimal(1) # represents w_i * f(x_i) (normalized)
319
+ # (b - l..b + l).each do |i|
320
+ # if i != b - l
321
+ # c = c.mult(-b * (b + l - i + 1), prec).div((i - b + l) * i, prec)
322
+ # end
323
+ # sum = sum.add(c.div(x - i, prec), prec)
324
+ # end
325
+ # --------------------------------------------
326
+
327
+ # Choose between BSM and BSGS based on total bit cost:
328
+ # BSM: (l * n_sig / prec) full-digit multiplications, each costing prec * log(prec)
329
+ # bit ops, total l * n_sig * log(prec).
330
+ # BSGS: l * prec bit ops with batch_size = log2(prec) (see below).
331
+ # Cross-over: n_sig * log(prec) > prec.
332
+ if x.n_significant_digits * prec.bit_length > prec
333
+ # Reduce full-precision multiplications/divisions using a Batched Evaluation
334
+ # inspired by the Baby-Step Giant-Step (BSGS) method.
335
+
336
+ # Normal BSGS uses batch_size = sqrt(l), but here the integer coefficients of the
337
+ # expanded prod { x - k } over a batch grow like (b + l)**batch_size, so a smaller
338
+ # batch keeps both the coefficient size and the per-batch evaluation cost low.
339
+ # batch_size = log2(prec) brings the total BSGS bit cost down to O(l * prec * log(log(prec))).
340
+ batch_size = prec.bit_length
341
+
342
+ # When expanding prod { x - k }, the coefficient of x**n might be huge.
343
+ # Increase internal calculation precision to avoid catastrophic cancellation.
344
+ # When x is within 10**-q of a node, batch_prod cancels by q more digits;
345
+ # without the extra digits the computed batch_prod can even collapse to
346
+ # exactly zero, and the division below would raise or produce NaN.
347
+ nearest_node_distance = x - x.round(0, BigDecimal::ROUND_HALF_UP)
348
+ near_node_digits = [1 - nearest_node_distance.exponent, 0].max
349
+ internal_xn_prec = prec + (Math.log10(b + l) * batch_size).ceil + near_node_digits
350
+ xn = [BigDecimal(1)]
351
+ xn << xn.last.mult(x, internal_xn_prec) while xn.size <= batch_size
352
+
353
+ c = BigDecimal(1)
354
+ sum = BigDecimal(0)
355
+ prod = BigDecimal(1)
356
+
357
+ # sum, c and prod are updated once per batch, so their rounding errors
358
+ # accumulate in proportion to the number of batches.
359
+ batch_count = (2 * l + 1 + shift) / batch_size + 1
360
+ accumulate_prec = prec + Math.log10(batch_count).ceil + 1
361
+
362
+ ((b - l)..(b + l)).to_a.each_slice(batch_size) do |batch_ks|
363
+ # Calculate prod{ x - k } in this batch
364
+ batch_prod, prod_coef = x_minus_k_prod_coef(batch_ks, xn, internal_xn_prec)
365
+
366
+ # Calculate coefficients of batch_prod / (x - k) using Synthetic Division (Ruffini's rule)
367
+ batch_coef = [0] * batch_ks.size
368
+ c_scale = 1r
369
+ batch_ks.each do |k|
370
+ c_scale = c_scale * (-b * (b + l - k + 1)) / ((k - b + l) * k) if k != b - l
371
+ rem = 0
372
+ (batch_ks.size - 1).downto(0) do |i|
373
+ quo = prod_coef[i + 1] + rem
374
+ rem = quo * k
375
+ batch_coef[i] += c_scale * quo
376
+ end
377
+ end
378
+
379
+ batch_sum = BigDecimal(0)
380
+ batch_coef.each_with_index do |coef, i|
381
+ batch_sum = batch_sum.add(xn[i].mult(coef.numerator, internal_xn_prec).div(coef.denominator, internal_xn_prec), internal_xn_prec)
382
+ end
383
+ # batch_prod loses relative accuracy when x is extremely close to a node in this
384
+ # batch. This is harmless: the same computed value is divided into sum here and
385
+ # multiplied into prod below, so the error cancels in the final prod * sum.
386
+ sum = sum.add(batch_sum.mult(c, accumulate_prec).div(batch_prod, accumulate_prec), accumulate_prec)
387
+ c = c.mult(c_scale.numerator, accumulate_prec).div(c_scale.denominator, accumulate_prec)
388
+ prod = prod.mult(batch_prod, accumulate_prec)
389
+ end
390
+
391
+ # Perform shift.times {|i| prod = prod.mult(x - i, prec) } with batch processing
392
+ shift.times.to_a.each_slice(batch_size) do |batch_ks|
393
+ shift_prod, _prod_coef = x_minus_k_prod_coef(batch_ks, xn, internal_xn_prec)
394
+ prod = prod.mult(shift_prod, accumulate_prec)
395
+ end
396
+ else
397
+ # Binary Splitting Method (BSM) for short-digit inputs.
398
+ # Scaling x by 10**frac_digits makes every node term x - i an exact integer,
399
+ # so the whole tree runs on Integer arithmetic. The scale cancels inside the
400
+ # fraction merges; only prod and sum need explicit rescaling.
401
+ frac_digits = [x.n_significant_digits - x.exponent, 0].max
402
+ s10 = 10**frac_digits
403
+ xs = x._decimal_shift(frac_digits).to_i
404
+ cap = drop_cap_bits(prec)
405
+
406
+ prod_factors = (b - l..b + l).map {|i| xs - i * s10 } + shift.times.map {|i| xs - i * s10 }
407
+ mantissa, dropped_exp2 = int_bsm_prod(prod_factors, cap)
408
+ prod = BigDecimal(mantissa)._decimal_shift(-frac_digits * prod_factors.size)
409
+ # prod is missing the dropped 2**dropped_exp2 factor and gamma is proportional
410
+ # to 1 / prod. Returning the compensation as exp2 lets the factorial doubling
411
+ # fold it into its own 2**exp2 channel instead of paying a power here.
412
+ exp2 = -dropped_exp2
413
+
414
+ # State represents a partial evaluation of the series as: [sum_num, mult_num, den]
415
+ # Conceptually, each state translates to the following mathematical expression:
416
+ # (sum_num / den) + (mult_num / den) * (rest_of_the_series)
417
+ #
418
+ # The initial state [denominator, numerator, denominator] simply represents:
419
+ # (denominator / denominator) + (numerator / denominator) * rest
420
+ # = 1 + (numerator / denominator) * rest
421
+ #
422
+ fractions = (b - l + 1..b + l).map do |i|
423
+ denominator = (xs - i * s10) * ((i - b + l) * i)
424
+ numerator = (xs - (i - 1) * s10) * (-b * (b + l - i + 1))
425
+ [denominator, numerator, denominator]
426
+ end
427
+
428
+ while fractions.size > 1
429
+ fractions = fractions.each_slice(2).map do |a, c|
430
+ c ||= [1, 0, 1]
431
+ # Merge operation for BSM:
432
+ # a[0]/a[2] + a[1]/a[2] * (c[0]/c[2] + c[1]/c[2] * rest)
433
+ # = (a[0]*c[2] + a[1]*c[0]) / (a[2]*c[2]) + (a[1]*c[1]) / (a[2]*c[2]) * rest
434
+ v0 = a[0] * c[2] + a[1] * c[0]
435
+ v1 = a[1] * c[1]
436
+ v2 = a[2] * c[2]
437
+ # Drop lower bits to avoid the integers growing too large; see drop_cap_bits.
438
+ # All three components share the shift, so the represented ratios are unchanged.
439
+ s = v2.bit_length - cap
440
+ if s > 0
441
+ v0 >>= s
442
+ v1 >>= s
443
+ v2 >>= s
444
+ end
445
+ [v0, v1, v2]
446
+ end
447
+ end
448
+ fraction = fractions.first
449
+ sum = BigDecimal((fraction[0] + fraction[1]) * s10).div(fraction[2] * (xs - (b - l) * s10), prec)
450
+ end
451
+
452
+ # Reconstruct Gamma(x_original) by reversing the scaling and applying shift formula
453
+ e = x - (b - l)
454
+ if e.frac == 0.5
455
+ # The factorial doubling path always has x = integer + 0.5. For this exponent shape,
456
+ # integer power and sqrt are much cheaper than the exp/log based fractional power.
457
+ power_part = BigDecimal(b).power(e.to_i, prec).mult(BigDecimal(b).sqrt(prec), prec)
458
+ else
459
+ power_part = BigDecimal(b).power(e, prec)
460
+ end
461
+ base = power_part.div(prod.mult(sum, prec), prec)
462
+ large_factorial_arg = b - l
463
+ small_factorial_arg = 2 * l
464
+ [base, large_factorial_arg, small_factorial_arg, exp2]
465
+ end
466
+
467
+ # Calculates bernoulli number.
468
+ # bns: calculated bernoulli numbers for memoization
469
+ def self.bernoulli(n, bns, prec)
470
+ return bns[0] ||= BigDecimal(1) if n == 0
471
+ return bns[1] ||= BigDecimal(-0.5) if n == 1
472
+ return bns[n] ||= BigDecimal(0) if n.odd?
473
+ bns[n] ||= (
474
+ comb = 1
475
+ sum = BigDecimal(0)
476
+ n.times do |i|
477
+ sum = sum.add(comb * bernoulli(i, bns, prec), prec)
478
+ comb = comb * (n - i + 1) / (i + 1)
479
+ end
480
+ sum.div(-n - 1, prec)
481
+ )
482
+ end
483
+
484
+ # Calculate log gamma using Stirling's asymptotic expansion.
485
+ # While the condition of this asymptotic expansion is x > prec * log(10) / 2 / pi,
486
+ # we'll use this method only when x is extremely large to reduce the cost of Bernoulli number generation.
487
+ def self.lgamma_stirling(x, prec)
488
+ x = BigDecimal(x)
489
+ y = (x * (BigMath.log(x, prec) - 1)).add(BigMath.log(2 * BigMath::PI(prec).div(x, prec), prec) / 2, prec)
490
+ bns = []
491
+ xn = x
492
+ x2 = x.mult(x, prec)
493
+ (1..).each do |k|
494
+ xn = xn.mult(x2, prec) if k != 1
495
+ d = bernoulli(2 * k, bns, prec).div(xn, prec).div(2 * k * (2 * k - 1), prec)
496
+ y = y.add(d, prec)
497
+ break if d.exponent < y.exponent - prec
498
+ end
499
+ y
500
+ end
501
+
502
+ # Returns sin(pi * x), for gamma reflection formula calculation
503
+ def self.sinpix(x, pi, prec)
504
+ x = x % 2
505
+ sign = x > 1 ? -1 : 1
506
+ x %= 1
507
+ x = 1 - x if x > 0.5 # to avoid sin(pi*x) loss of precision for x close to 1
508
+ sign * BigMath.sin(x.mult(pi, prec), prec)
509
+ end
510
+ end
511
+
512
+ private_constant :Gamma
513
+ end
@@ -596,28 +596,8 @@ module BigMath
596
596
  # #=> "0.84270079294971486934122063508261e0"
597
597
  #
598
598
  def erf(x, prec)
599
- prec = BigDecimal::Internal.coerce_validate_prec(prec, :erf)
600
- x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erf)
601
- return BigDecimal::Internal.nan_computation_result if x.nan?
602
- return BigDecimal(x.infinite?) if x.infinite?
603
- return BigDecimal(0) if x == 0
604
- return -erf(-x, prec) if x < 0
605
- return BigDecimal(1) if x > 5000000000 # erf(5000000000) > 1 - 1e-10000000000000000000
606
-
607
- if x > 8
608
- xf = x.to_f
609
- log10_erfc = -xf ** 2 / Math.log(10) - Math.log10(xf * Math::PI ** 0.5)
610
- erfc_prec = [prec + log10_erfc.ceil, 1].max
611
- erfc = _erfc_asymptotic(x, erfc_prec)
612
- return BigDecimal(1).sub(erfc, prec) if erfc
613
- end
614
-
615
- prec2 = prec + BigDecimal::Internal::EXTRA_PREC
616
- x_smallprec = x.mult(1, Integer.sqrt(prec2) / 2)
617
- # Taylor series of x with small precision is fast
618
- erf1 = _erf_taylor(x_smallprec, BigDecimal(0), BigDecimal(0), prec2)
619
- # Taylor series converges quickly for small x
620
- _erf_taylor(x - x_smallprec, x_smallprec, erf1, prec2).mult(1, prec)
599
+ require 'bigdecimal/math/erf'
600
+ Erf.erf(x, prec)
621
601
  end
622
602
 
623
603
  # call-seq:
@@ -632,87 +612,8 @@ module BigMath
632
612
  # #=> "0.20884875837625447570007862949578e-44"
633
613
  #
634
614
  def erfc(x, prec)
635
- prec = BigDecimal::Internal.coerce_validate_prec(prec, :erfc)
636
- x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erfc)
637
- return BigDecimal::Internal.nan_computation_result if x.nan?
638
- return BigDecimal(1 - x.infinite?) if x.infinite?
639
- return BigDecimal(1).sub(erf(x, prec + BigDecimal::Internal::EXTRA_PREC), prec) if x < 0.5
640
- return BigDecimal::Internal.underflow_computation_result if x > 5000000000 # erfc(5000000000) < 1e-10000000000000000000 (underflow)
641
-
642
- if x >= 8
643
- y = _erfc_asymptotic(x, prec)
644
- return y.mult(1, prec) if y
645
- end
646
-
647
- # erfc(x) = 1 - erf(x) < exp(-x**2)/x/sqrt(pi)
648
- # Precision of erf(x) needs about log10(exp(-x**2)/x/sqrt(pi)) extra digits
649
- log10 = 2.302585092994046
650
- xf = x.to_f
651
- high_prec = prec + BigDecimal::Internal::EXTRA_PREC + ((xf**2 + Math.log(xf) + Math.log(Math::PI)/2) / log10).ceil
652
- BigDecimal(1).sub(erf(x, high_prec), prec)
653
- end
654
-
655
- # Calculates erf(x + a)
656
- private_class_method def _erf_taylor(x, a, erf_a, prec) # :nodoc:
657
- return erf_a if x.zero?
658
- # Let f(x+a) = erf(x+a)*exp((x+a)**2)*sqrt(pi)/2
659
- # = c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...
660
- # f'(x+a) = 1+2*(x+a)*f(x+a)
661
- # f'(x+a) = c1 + 2*c2*x + 3*c3*x**2 + 4*c4*x**3 + 5*c5*x**4 + ...
662
- # = 1+2*(x+a)*(c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...)
663
- # therefore,
664
- # c0 = f(a)
665
- # c1 = 2 * a * c0 + 1
666
- # c2 = (2 * c0 + 2 * a * c1) / 2
667
- # c3 = (2 * c1 + 2 * a * c2) / 3
668
- # c4 = (2 * c2 + 2 * a * c3) / 4
669
- #
670
- # All coefficients are positive when a >= 0
671
-
672
- scale = BigDecimal(2).div(sqrt(PI(prec), prec), prec)
673
- c_prev = erf_a.div(scale.mult(exp(-a*a, prec), prec), prec)
674
- c_next = (2 * a * c_prev).add(1, prec).mult(x, prec)
675
- sum = c_prev.add(c_next, prec)
676
-
677
- 2.step do |k|
678
- cn = (c_prev.mult(x, prec) + a * c_next).mult(2, prec).mult(x, prec).div(k, prec)
679
- sum = sum.add(cn, prec)
680
- c_prev, c_next = c_next, cn
681
- break if [c_prev, c_next].all? { |c| c.zero? || (c.exponent < sum.exponent - prec) }
682
- end
683
- value = sum.mult(scale.mult(exp(-(x + a).mult(x + a, prec), prec), prec), prec)
684
- value > 1 ? BigDecimal(1) : value
685
- end
686
-
687
- private_class_method def _erfc_asymptotic(x, prec) # :nodoc:
688
- # Let f(x) = erfc(x)*sqrt(pi)*exp(x**2)/2
689
- # f(x) satisfies the following differential equation:
690
- # 2*x*f(x) = f'(x) + 1
691
- # From the above equation, we can derive the following asymptotic expansion:
692
- # f(x) = (0..kmax).sum { (-1)**k * (2*k)! / 4**k / k! / x**(2*k)) } / x
693
-
694
- # This asymptotic expansion does not converge.
695
- # But if there is a k that satisfies (2*k)! / 4**k / k! / x**(2*k) < 10**(-prec),
696
- # It is enough to calculate erfc within the given precision.
697
- # Using Stirling's approximation, we can simplify this condition to:
698
- # sqrt(2)/2 + k*log(k) - k - 2*k*log(x) < -prec*log(10)
699
- # and the left side is minimized when k = x**2.
700
- prec += BigDecimal::Internal::EXTRA_PREC
701
- xf = x.to_f
702
- kmax = (1..(xf ** 2).floor).bsearch do |k|
703
- Math.log(2) / 2 + k * Math.log(k) - k - 2 * k * Math.log(xf) < -prec * Math.log(10)
704
- end
705
- return unless kmax
706
-
707
- sum = BigDecimal(1)
708
- # To calculate `exp(x2, prec)`, x2 needs extra log10(x**2) digits of precision
709
- x2 = x.mult(x, prec + (2 * Math.log10(xf)).ceil)
710
- d = BigDecimal(1)
711
- (1..kmax).each do |k|
712
- d = d.div(x2, prec).mult(1 - 2 * k, prec).div(2, prec)
713
- sum = sum.add(d, prec)
714
- end
715
- sum.div(exp(x2, prec).mult(PI(prec).sqrt(prec), prec), prec).div(x, prec)
615
+ require 'bigdecimal/math/erf'
616
+ Erf.erfc(x, prec)
716
617
  end
717
618
 
718
619
  # call-seq:
@@ -725,22 +626,8 @@ module BigMath
725
626
  # #=> "0.17724538509055160272981674833411e1"
726
627
  #
727
628
  def gamma(x, prec)
728
- prec = BigDecimal::Internal.coerce_validate_prec(prec, :gamma)
729
- x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :gamma)
730
- prec2 = prec + BigDecimal::Internal::EXTRA_PREC
731
- if x < 0.5
732
- raise Math::DomainError, 'Numerical argument is out of domain - gamma' if x.frac.zero?
733
-
734
- # Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z)
735
- pi = PI(prec2)
736
- sin = _sinpix(x, pi, prec2)
737
- return pi.div(gamma(1 - x, prec2).mult(sin, prec2), prec)
738
- elsif x.frac.zero? && x < 1000 * prec
739
- return _gamma_positive_integer(x, prec2).mult(1, prec)
740
- end
741
-
742
- a, sum = _gamma_spouge_sum_part(x, prec2)
743
- (x + (a - 1)).power(x - 0.5, prec2).mult(BigMath.exp(1 - x, prec2), prec2).mult(sum, prec)
629
+ require 'bigdecimal/math/gamma'
630
+ Gamma.gamma(x, prec)
744
631
  end
745
632
 
746
633
  # call-seq:
@@ -753,106 +640,8 @@ module BigMath
753
640
  # #=> [0.57236494292470008707171367567653e0, 1]
754
641
  #
755
642
  def lgamma(x, prec)
756
- prec = BigDecimal::Internal.coerce_validate_prec(prec, :lgamma)
757
- x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :lgamma)
758
- prec2 = prec + BigDecimal::Internal::EXTRA_PREC
759
- if x < 0.5
760
- return [BigDecimal::INFINITY, 1] if x.frac.zero?
761
-
762
- loop do
763
- # Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z)
764
- pi = PI(prec2)
765
- sin = _sinpix(x, pi, prec2)
766
- log_gamma = BigMath.log(pi, prec2).sub(lgamma(1 - x, prec2).first + BigMath.log(sin.abs, prec2), prec)
767
- return [log_gamma, sin > 0 ? 1 : -1] if prec2 + log_gamma.exponent > prec + BigDecimal::Internal::EXTRA_PREC
768
-
769
- # Retry with higher precision if loss of significance is too large
770
- prec2 = prec2 * 3 / 2
771
- end
772
- elsif x.frac.zero? && x < 1000 * prec
773
- log_gamma = BigMath.log(_gamma_positive_integer(x, prec2), prec)
774
- [log_gamma, 1]
775
- else
776
- # if x is close to 1 or 2, increase precision to reduce loss of significance
777
- diff1_exponent = (x - 1).exponent
778
- diff2_exponent = (x - 2).exponent
779
- extremely_near_one = diff1_exponent < -prec2
780
- extremely_near_two = diff2_exponent < -prec2
781
-
782
- if extremely_near_one || extremely_near_two
783
- # If x is extreamely close to base = 1 or 2, linear interpolation is accurate enough.
784
- # Taylor expansion at x = base is: (x - base) * digamma(base) + (x - base) ** 2 * trigamma(base) / 2 + ...
785
- # And we can ignore (x - base) ** 2 and higher order terms.
786
- base = extremely_near_one ? 1 : 2
787
- d = BigDecimal(1)._decimal_shift(1 - prec2)
788
- log_gamma_d, sign = lgamma(base + d, prec2)
789
- return [log_gamma_d.mult(x - base, prec2).div(d, prec), sign]
790
- end
791
-
792
- prec2 += [-diff1_exponent, -diff2_exponent, 0].max
793
- a, sum = _gamma_spouge_sum_part(x, prec2)
794
- log_gamma = BigMath.log(sum, prec2).add((x - 0.5).mult(BigMath.log(x.add(a - 1, prec2), prec2), prec2) + 1 - x, prec)
795
- [log_gamma, 1]
796
- end
797
- end
798
-
799
- # Returns sum part: sqrt(2*pi) and c[k]/(x+k) terms of Spouge's approximation
800
- private_class_method def _gamma_spouge_sum_part(x, prec) # :nodoc:
801
- x -= 1
802
- # Spouge's approximation
803
- # x! = (x + a)**(x + 0.5) * exp(-x - a) * (sqrt(2 * pi) + (1..a - 1).sum{|k| c[k] / (x + k) } + epsilon)
804
- # where c[k] = (-1)**k * (a - k)**(k - 0.5) * exp(a - k) / (k - 1)!
805
- # and epsilon is bounded by a**(-0.5) * (2 * pi) ** (-a - 0.5)
806
-
807
- # Estimate required a for given precision
808
- a = (prec / Math.log10(2 * Math::PI)).ceil
809
-
810
- # Calculate exponent of c[k] in low precision to estimate required precision
811
- low_prec = 16
812
- log10f = Math.log(10)
813
- x_low_prec = x.mult(1, low_prec)
814
- loggamma_k = 0
815
- ck_exponents = (1..a-1).map do |k|
816
- loggamma_k += Math.log10(k - 1) if k > 1
817
- -loggamma_k - k / log10f + (k - 0.5) * Math.log10(a - k) - BigDecimal::Internal.float_log(x_low_prec.add(k, low_prec)) / log10f
818
- end
819
-
820
- # Estimate exponent of sum by Stirling's approximation
821
- approx_sum_exponent = x < 1 ? -Math.log10(a) / 2 : Math.log10(2 * Math::PI) / 2 + x_low_prec.add(0.5, low_prec) * Math.log10(x_low_prec / x_low_prec.add(a, low_prec))
822
-
823
- # Determine required precision of c[k]
824
- prec2 = [ck_exponents.max.ceil - approx_sum_exponent.floor, 0].max + prec
825
-
826
- einv = BigMath.exp(-1, prec2)
827
- sum = (PI(prec) * 2).sqrt(prec).mult(BigMath.exp(-a, prec), prec)
828
- y = BigDecimal(1)
829
- (1..a - 1).each do |k|
830
- # c[k] = (-1)**k * (a - k)**(k - 0.5) * exp(-k) / (k-1)! / (x + k)
831
- y = y.div(1 - k, prec2) if k > 1
832
- y = y.mult(einv, prec2)
833
- z = y.mult(BigDecimal((a - k) ** k), prec2).div(BigDecimal(a - k).sqrt(prec2).mult(x.add(k, prec2), prec2), prec2)
834
- # sum += c[k] / (x + k)
835
- sum = sum.add(z, prec2)
836
- end
837
- [a, sum]
838
- end
839
-
840
- private_class_method def _gamma_positive_integer(x, prec) # :nodoc:
841
- return x if x == 1
842
- numbers = (1..x - 1).map {|i| BigDecimal(i) }
843
- while numbers.size > 1
844
- numbers = numbers.each_slice(2).map {|a, b| b ? a.mult(b, prec) : a }
845
- end
846
- numbers.first
847
- end
848
-
849
- # Returns sin(pi * x), for gamma reflection formula calculation
850
- private_class_method def _sinpix(x, pi, prec) # :nodoc:
851
- x = x % 2
852
- sign = x > 1 ? -1 : 1
853
- x %= 1
854
- x = 1 - x if x > 0.5 # to avoid sin(pi*x) loss of precision for x close to 1
855
- sign * sin(x.mult(pi, prec), prec)
643
+ require 'bigdecimal/math/gamma'
644
+ Gamma.lgamma(x, prec)
856
645
  end
857
646
 
858
647
  # call-seq:
data/sig/big_decimal.rbs CHANGED
@@ -1237,7 +1237,9 @@ module Kernel
1237
1237
  # Raises an exception if `value` evaluates to a Float and `digits` is larger
1238
1238
  # than Float::DIG + 1.
1239
1239
  #
1240
- def self?.BigDecimal: (real | string | BigDecimal initial, ?int digits, ?exception: bool) -> BigDecimal
1240
+ def self?.BigDecimal: (real | string | BigDecimal initial, ?int digits, ?exception: true) -> BigDecimal
1241
+ | (real | string | BigDecimal initial, ?int digits, exception: bool) -> BigDecimal?
1242
+ | (untyped initial, ?untyped digits, ?exception: bool) -> BigDecimal?
1241
1243
  end
1242
1244
 
1243
1245
  %a{annotate:rdoc:skip}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bigdecimal
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.2
4
+ version: 4.1.3
5
5
  platform: java
6
6
  authors:
7
7
  - Kenta Murata
@@ -25,6 +25,8 @@ files:
25
25
  - lib/bigdecimal/jacobian.rb
26
26
  - lib/bigdecimal/ludcmp.rb
27
27
  - lib/bigdecimal/math.rb
28
+ - lib/bigdecimal/math/erf.rb
29
+ - lib/bigdecimal/math/gamma.rb
28
30
  - lib/bigdecimal/newton.rb
29
31
  - lib/bigdecimal/util.rb
30
32
  - sample/linear.rb