bigdecimal-pure 0.1.0 → 0.2.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,582 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BigDecimalPure
4
+ # The arithmetic behind BigMath, on Integers in fixed point: a real r is the
5
+ # Integer r * 10**s for a scale s. Each routine takes a BigDecimal and a
6
+ # number of significant digits w and returns a BigDecimal with at least w
7
+ # correct digits; BigMath asks for its precision plus a margin and rounds
8
+ # once, in the current rounding mode. The C version rounds after each step
9
+ # instead, so the two agree except when the exact value lies within about
10
+ # 10**-(prec+20) of a rounding boundary.
11
+ #
12
+ # Series with alternating terms keep the terms' magnitudes, because Integer
13
+ # division rounds a negative quotient down and a term would never reach 0.
14
+ module Calc # :nodoc: all
15
+ module_function
16
+
17
+ # Digits computed beyond the precision BigMath returns.
18
+ GUARD = 24
19
+
20
+ @constants = {}
21
+ @bernoulli = []
22
+
23
+ def pow10(n) = BigDecimal._pow10(n)
24
+ def ndigits(n) = BigDecimal._ndigits(n.abs)
25
+
26
+ # x * 10**s, truncated toward zero.
27
+ def fixed(x, s)
28
+ _, sign, coef, exp = x._parts
29
+ e = exp + s
30
+ v = e >= 0 ? coef * pow10(e) : coef / pow10(-e)
31
+ sign.negative? ? -v : v
32
+ end
33
+
34
+ # m * 10**-s, exactly.
35
+ def decimal(m, s) = BigDecimal._make(m.negative? ? -1 : 1, m.abs, -s)
36
+
37
+ # Exact sums and products of finite values, untouched by BigDecimal.limit.
38
+ def plus(a, b)
39
+ _, sa, ca, ea = a._parts
40
+ _, sb, cb, eb = b._parts
41
+ e = [ea, eb].min
42
+ sum = (sa * ca * pow10(ea - e)) + (sb * cb * pow10(eb - e))
43
+ BigDecimal._make(sum.negative? ? -1 : 1, sum.abs, e)
44
+ end
45
+
46
+ def minus(a, b) = plus(a, -b)
47
+
48
+ def times(a, b)
49
+ _, sa, ca, ea = a._parts
50
+ _, sb, cb, eb = b._parts
51
+ BigDecimal._make(sa * sb, ca * cb, ea + eb)
52
+ end
53
+
54
+ def one = BigDecimal(1)
55
+
56
+ # ---- constants, cached at the largest scale asked for so far ----------
57
+
58
+ def constant(name, s)
59
+ digits, value = @constants[name]
60
+ if digits.nil? || digits < s
61
+ digits = [s, (digits || 0) + ((digits || 0) / 2)].max
62
+ guard = ndigits(digits) + 4
63
+ value = yield(digits + guard) / pow10(guard)
64
+ @constants[name] = [digits, value]
65
+ end
66
+ value / pow10(digits - s)
67
+ end
68
+
69
+ # atan(1/n) * 10**s: 1/n - 1/(3n^3) + 1/(5n^5) - ...
70
+ def atan_inv(n, s)
71
+ n2 = n * n
72
+ term = pow10(s) / n
73
+ sum = term
74
+ k = 1
75
+ until term.zero?
76
+ term /= n2
77
+ k += 2
78
+ sum += (k % 4 == 1 ? 1 : -1) * (term / k)
79
+ end
80
+ sum
81
+ end
82
+
83
+ # atanh(1/n) * 10**s: 1/n + 1/(3n^3) + 1/(5n^5) + ...
84
+ def atanh_inv(n, s)
85
+ n2 = n * n
86
+ term = pow10(s) / n
87
+ sum = term
88
+ k = 1
89
+ until term.zero?
90
+ term /= n2
91
+ k += 2
92
+ sum += term / k
93
+ end
94
+ sum
95
+ end
96
+
97
+ # Machin's formula.
98
+ def pi(s) = constant(:pi, s) { |d| (16 * atan_inv(5, d)) - (4 * atan_inv(239, d)) }
99
+
100
+ # ln 2 = 18 atanh(1/26) - 2 atanh(1/4801) + 8 atanh(1/8749).
101
+ def ln2(s)
102
+ constant(:ln2, s) { |d| (18 * atanh_inv(26, d)) - (2 * atanh_inv(4801, d)) + (8 * atanh_inv(8749, d)) }
103
+ end
104
+
105
+ # ln 10 = 3 ln 2 + ln 1.25, and ln 1.25 = 2 atanh(1/9).
106
+ def ln10(s) = constant(:ln10, s) { |d| (3 * ln2(d)) + (2 * atanh_inv(9, d)) }
107
+
108
+ def pi_decimal(w) = decimal(pi(w + 2), w + 2)
109
+
110
+ # ---- exp and log ------------------------------------------------------
111
+
112
+ # e**x for a finite x with |x| < 10**21, to w digits: e**x = 10**k * e**r
113
+ # with r = x - k ln 10 in [0, ln 10).
114
+ def exp(x, w)
115
+ return one if x.zero?
116
+ # For a tiny x, 1 + x + x**2/2 already has w digits, and a fixed-point
117
+ # computation at w digits would round the x away.
118
+ return plus(one, plus(x, times(times(x, x), BigDecimal('0.5')))) if x.exponent < -(w / 2) - 2
119
+
120
+ s0 = [x.exponent, 0].max + 10
121
+ k = fixed(x, s0).div(ln10(s0))
122
+ s = w + ndigits(k) + 12
123
+ r = fixed(x, s) - (k * ln10(s))
124
+ BigDecimal._make(1, exp_fixed(r, s), k - s)
125
+ end
126
+
127
+ # e**(r / 10**s) * 10**s for |r / 10**s| below a few units: halve the
128
+ # argument j times, sum the Taylor series, square j times.
129
+ def exp_fixed(r, s)
130
+ return pow10(2 * s) / exp_fixed(-r, s) if r.negative?
131
+
132
+ halvings = [Integer.sqrt(3 * s), 4].max
133
+ s2 = s + ndigits(s) + (halvings / 3) + 4
134
+ unit = pow10(s2)
135
+ x = (r * pow10(s2 - s)) >> halvings
136
+ sum = unit
137
+ term = unit
138
+ n = 0
139
+ loop do
140
+ n += 1
141
+ term = term * x / (unit * n)
142
+ break if term.zero?
143
+
144
+ sum += term
145
+ end
146
+ halvings.times { sum = sum * sum / unit }
147
+ sum / pow10(s2 - s)
148
+ end
149
+
150
+ # ln x for a finite x > 0, x != 1, to w digits, by
151
+ # ln t = 2 atanh((t - 1) / (t + 1)) on t near 1.
152
+ def log(x, w)
153
+ return BigDecimal(0) if x == 1
154
+
155
+ if x > BigDecimal('0.5') && x < BigDecimal(2)
156
+ # |ln x| can be as small as |x - 1|: scale for that many digits.
157
+ xr = x.to_r
158
+ d = xr - 1
159
+ magnitude = ndigits(d.numerator) - ndigits(d.denominator)
160
+ s = w + ndigits(w) + 10 + [-magnitude, 0].max
161
+ z = (d * pow10(s) / (xr + 1)).round
162
+ return decimal(2 * atanh_fixed(z, s), s)
163
+ end
164
+ # x = m * 10**e with m in [1, 10), m = t * 2**k with t near 1;
165
+ # |ln x| >= ln 2 here, so a fixed scale gives w digits.
166
+ e = x.exponent - 1
167
+ m = x._decimal_shift(-e)
168
+ k = Math.log2(m.to_f).round
169
+ t = m.to_r / (1 << k)
170
+ s = w + ndigits(w) + ndigits(e.abs + k + 1) + 10
171
+ z = ((t - 1) * pow10(s) / (t + 1)).round
172
+ decimal((2 * atanh_fixed(z, s)) + (k * ln2(s)) + (e * ln10(s)), s)
173
+ end
174
+
175
+ # atanh(z / 10**s) * 10**s for |z / 10**s| <= 1/3.
176
+ def atanh_fixed(z, s)
177
+ return -atanh_fixed(-z, s) if z.negative?
178
+
179
+ unit = pow10(s)
180
+ z2 = z * z / unit
181
+ term = z
182
+ sum = z
183
+ k = 1
184
+ loop do
185
+ term = term * z2 / unit
186
+ break if term.zero?
187
+
188
+ k += 2
189
+ sum += term / k
190
+ end
191
+ sum
192
+ end
193
+
194
+ # ---- roots, exactly ---------------------------------------------------
195
+
196
+ # [root, exponent, exact]: root * 10**exponent is sqrt(x) rounded down
197
+ # to at least w digits, exact when it is the whole square root.
198
+ def sqrt_digits(x, w)
199
+ _, _, coef, exp = x._parts
200
+ shift = [(2 * w) + 2 - ndigits(coef), 0].max
201
+ shift += 1 if (exp - shift).odd?
202
+ n = coef * pow10(shift)
203
+ root = Integer.sqrt(n)
204
+ [root, (exp - shift) / 2, root * root == n]
205
+ end
206
+
207
+ def cbrt_digits(x, w)
208
+ _, _, coef, exp = x._parts
209
+ shift = [(3 * w) + 3 - ndigits(coef), 0].max
210
+ shift += 1 until ((exp - shift) % 3).zero?
211
+ n = coef * pow10(shift)
212
+ root = icbrt(n)
213
+ [root, (exp - shift) / 3, root * root * root == n]
214
+ end
215
+
216
+ def icbrt(n)
217
+ return n if n < 2
218
+
219
+ x = 1 << ((n.bit_length + 2) / 3)
220
+ loop do
221
+ y = ((2 * x) + (n / (x * x))) / 3
222
+ return x if y >= x
223
+
224
+ x = y
225
+ end
226
+ end
227
+
228
+ def sqrt(x, w)
229
+ root, exp, = sqrt_digits(x, w)
230
+ BigDecimal._make(1, root, exp)
231
+ end
232
+
233
+ # ---- trigonometry -----------------------------------------------------
234
+
235
+ # [q, r2, s]: x = q * pi/2 + r with r2 = 2r * 10**s, |r| <= pi/4, and r2
236
+ # good to w + 10 digits. The scale grows until the cancellation in
237
+ # x - q pi/2 leaves enough digits.
238
+ def reduce_half_pi(x, w)
239
+ e = [x.exponent, 0].max
240
+ extra = 0
241
+ loop do
242
+ s = w + e + extra + ndigits(w) + 10
243
+ p = pi(s)
244
+ xs = fixed(x, s)
245
+ q = ((4 * xs) + p).div(2 * p)
246
+ r2 = (2 * xs) - (q * p)
247
+ deficit = (w + 10) - (ndigits(r2) - ndigits(q) - 2)
248
+ deficit = w + 10 + s if r2.zero?
249
+ return [q, r2, s] if deficit <= 0
250
+
251
+ extra += deficit + 5
252
+ end
253
+ end
254
+
255
+ # sin or cos of a finite nonzero x, to w digits.
256
+ def sin_cos(x, w, cosine)
257
+ q, r2, s = reduce_half_pi(x, w)
258
+ n = q % 4
259
+ use_cos = cosine ? n.even? : n.odd?
260
+ negative = cosine ? [1, 2].include?(n) : n >= 2
261
+ value, scale = sin_or_cos(r2, s, w, use_cos)
262
+ decimal(negative ? -value : value, scale)
263
+ end
264
+
265
+ # [value, scale]: sin(r) or cos(r) * 10**scale for r2 = 2r * 10**s.
266
+ def sin_or_cos(r2, s, w, use_cos)
267
+ size = ndigits(r2) - s # |r| is about 10**size
268
+ # sin(r) is about r, cos(r) - 1 about r**2/2: keep those digits too.
269
+ t = w + ndigits(w) + 10 + (use_cos ? [-2 * size, 0].max : [-size, 0].max)
270
+ a = r2.abs * pow10(t) / (2 * pow10(s))
271
+ return [cos_fixed(a, t), t] if use_cos
272
+
273
+ [r2.negative? ? -sin_fixed(a, t) : sin_fixed(a, t), t]
274
+ end
275
+
276
+ def sin_fixed(a, t)
277
+ unit = pow10(t)
278
+ a2 = a * a / unit
279
+ term = a
280
+ sum = a
281
+ k = 1
282
+ loop do
283
+ term = term * a2 / (unit * (k + 1) * (k + 2))
284
+ break if term.zero?
285
+
286
+ k += 2
287
+ sum += k % 4 == 1 ? term : -term
288
+ end
289
+ sum
290
+ end
291
+
292
+ def cos_fixed(a, t)
293
+ unit = pow10(t)
294
+ a2 = a * a / unit
295
+ term = unit
296
+ sum = unit
297
+ k = 0
298
+ loop do
299
+ term = term * a2 / (unit * (k + 1) * (k + 2))
300
+ break if term.zero?
301
+
302
+ k += 2
303
+ sum += k % 4 == 2 ? -term : term
304
+ end
305
+ sum
306
+ end
307
+
308
+ # tan of a finite nonzero x, to w digits.
309
+ def tan(x, w)
310
+ q, r2, s = reduce_half_pi(x, w + 5)
311
+ sin_r, sin_scale = sin_or_cos(r2, s, w + 5, false)
312
+ cos_r, cos_scale = sin_or_cos(r2, s, w + 5, true)
313
+ sine = decimal(sin_r, sin_scale)
314
+ cosine = decimal(cos_r, cos_scale)
315
+ # tan(r + q pi/2) is tan r for even q and -cot r for odd q.
316
+ q.even? ? sine.div(cosine, w + 5) : -cosine.div(sine, w + 5)
317
+ end
318
+
319
+ # atan of a finite x > 0, to w digits.
320
+ def atan(x, w)
321
+ s = w + ndigits(w) + 10
322
+ if x > one
323
+ # pi/2 - atan(1/x), which is at least pi/4.
324
+ inverse = pow10(2 * s) / fixed(x, s)
325
+ return decimal((pi(s) / 2) - atan_fixed(inverse, s), s)
326
+ end
327
+ s += [-x.exponent, 0].max
328
+ decimal(atan_fixed(fixed(x, s), s), s)
329
+ end
330
+
331
+ # atan(a / 10**s) * 10**s for 0 <= a / 10**s <= 1: halve the angle with
332
+ # atan y = 2 atan(y / (1 + sqrt(1 + y**2))) until y < 0.01, then Taylor.
333
+ def atan_fixed(a, s)
334
+ guard = ndigits(s) + 4
335
+ s2 = s + guard
336
+ unit = pow10(s2)
337
+ y = a * pow10(guard)
338
+ halvings = 0
339
+ while y > unit / 100
340
+ y = y * unit / (unit + Integer.sqrt((unit * unit) + (y * y)))
341
+ halvings += 1
342
+ end
343
+ y2 = y * y / unit
344
+ term = y
345
+ sum = y
346
+ k = 1
347
+ loop do
348
+ term = term * y2 / unit
349
+ break if term.zero?
350
+
351
+ k += 2
352
+ sum += (k % 4 == 1 ? 1 : -1) * (term / k)
353
+ end
354
+ (sum << halvings) / pow10(guard)
355
+ end
356
+
357
+ # ---- hyperbolic functions for |x| < 1, by their series ----------------
358
+
359
+ # sinh(x) for finite 0 < |x| < 1, to w digits.
360
+ def sinh_small(x, w)
361
+ t = w + ndigits(w) + 10 + [-x.exponent, 0].max
362
+ a = fixed(x.abs, t)
363
+ unit = pow10(t)
364
+ a2 = a * a / unit
365
+ term = a
366
+ sum = a
367
+ k = 1
368
+ loop do
369
+ term = term * a2 / (unit * (k + 1) * (k + 2))
370
+ break if term.zero?
371
+
372
+ k += 2
373
+ sum += term
374
+ end
375
+ decimal(x.negative? ? -sum : sum, t)
376
+ end
377
+
378
+ # cosh(x) for finite |x| < 1, to w digits.
379
+ def cosh_small(x, w)
380
+ t = w + ndigits(w) + 10 + [-2 * x.exponent, 0].max
381
+ a = fixed(x.abs, t)
382
+ unit = pow10(t)
383
+ a2 = a * a / unit
384
+ term = unit
385
+ sum = unit
386
+ k = 0
387
+ loop do
388
+ term = term * a2 / (unit * (k + 1) * (k + 2))
389
+ break if term.zero?
390
+
391
+ k += 2
392
+ sum += term
393
+ end
394
+ decimal(sum, t)
395
+ end
396
+
397
+ # ---- the error function -----------------------------------------------
398
+
399
+ # erf(x) * 10**digits for a finite x > 0, with an absolute error of a few
400
+ # units, from the series with only positive terms
401
+ # erf(x) = 2/sqrt(pi) * exp(-x**2) * sum_n 2**n x**(2n+1) / (1*3*...*(2n+1)).
402
+ def erf_fixed(x, digits)
403
+ grow = ((x.to_f**2) / Math.log(10)).ceil # the sum reaches about 10**grow
404
+ s = digits + grow + ndigits(digits + grow) + 10 + [-x.exponent, 0].max
405
+ unit = pow10(s)
406
+ xs = fixed(x, s)
407
+ x2 = 2 * xs * xs / unit
408
+ term = xs
409
+ sum = xs
410
+ n = 0
411
+ loop do
412
+ n += 1
413
+ term = term * x2 / (unit * ((2 * n) + 1))
414
+ break if term.zero?
415
+
416
+ sum += term
417
+ end
418
+ w = digits + ndigits(digits) + 10
419
+ factor = exp(-times(x, x), w).mult(2, w).div(sqrt(pi_decimal(w + 2), w + 2), w)
420
+ fixed(decimal(sum, s).mult(factor, w + grow + 10), digits)
421
+ end
422
+
423
+ # erf(x) for a finite x > 0, to w digits, and nil when erf(x) is 1 to
424
+ # more than w digits.
425
+ def erf(x, w)
426
+ return nil if x.to_f**2 > (w + 5) * Math.log(10)
427
+
428
+ digits = w + 5 + [-x.exponent, 0].max
429
+ decimal(erf_fixed(x, digits), digits)
430
+ end
431
+
432
+ # erfc(x) for a finite x > 0, to w digits.
433
+ def erfc(x, w)
434
+ xf = x.to_f
435
+ if xf < 0.5
436
+ # 1 - erf(x) is about 1 - 1.13 x: keep x's digits as well.
437
+ digits = w + 5 + [-x.exponent, 0].max
438
+ return decimal(pow10(digits) - erf_fixed(x, digits), digits)
439
+ end
440
+ return erfc_asymptotic(x, w) if xf * xf > (w + 12) * Math.log(10)
441
+
442
+ # 1 - erf(x) loses about log10(erfc(x)) digits to cancellation.
443
+ lost = ((xf * xf) + Math.log(xf * Math.sqrt(Math::PI))) / Math.log(10)
444
+ digits = w + 10 + lost.ceil
445
+ decimal(pow10(digits) - erf_fixed(x, digits), digits)
446
+ end
447
+
448
+ # erfc(x) = exp(-x**2) / (x sqrt(pi)) * sum_n (-1)**n (2n-1)!! / (2x**2)**n
449
+ # for x**2 large enough that the smallest term is below 10**-(w+12).
450
+ def erfc_asymptotic(x, w)
451
+ s = w + ndigits(w) + 12
452
+ unit = pow10(s)
453
+ x2 = fixed(times(times(x, x), BigDecimal(2)), s)
454
+ term = unit
455
+ sum = unit
456
+ n = 0
457
+ loop do
458
+ n += 1
459
+ smaller = term * ((2 * n) - 1) * unit / x2
460
+ break if smaller.zero? || smaller >= term
461
+
462
+ term = smaller
463
+ sum += n.odd? ? -term : term
464
+ end
465
+ w2 = w + 10
466
+ root_pi = sqrt(pi_decimal(w2 + 2), w2 + 2)
467
+ exp(-times(x, x), w2).mult(decimal(sum, s), w2).div(x.mult(root_pi, w2), w2)
468
+ end
469
+
470
+ # ---- gamma --------------------------------------------------------------
471
+
472
+ # B_2, B_4, ..., B_2n as Rationals, from the tangent numbers
473
+ # (Brent and Harvey): B_2k = (-1)**(k-1) * 2k * T_k / (4**k * (4**k - 1)).
474
+ def bernoulli(count)
475
+ return @bernoulli if @bernoulli.size >= count
476
+
477
+ n = [count, (@bernoulli.size * 3 / 2) + 8].max
478
+ t = Array.new(n + 1, 0)
479
+ t[1] = 1
480
+ (2..n).each { |k| t[k] = (k - 1) * t[k - 1] }
481
+ (2..n).each do |k| # rubocop:disable Style/CombinableLoops -- the first loop must finish first
482
+ (k..n).each { |j| t[j] = ((j - k) * t[j - 1]) + ((j - k + 2) * t[j]) }
483
+ end
484
+ @bernoulli = (1..n).map do |k|
485
+ four = 1 << (2 * k)
486
+ value = Rational(2 * k * t[k], four * (four - 1))
487
+ k.odd? ? value : -value
488
+ end
489
+ end
490
+
491
+ # ln(2 pi) / 2.
492
+ def half_ln_2pi(s)
493
+ constant(:half_ln_2pi, s) do |d|
494
+ fixed(log(times(pi_decimal(d + 5), BigDecimal(2)), d + 5), d) / 2
495
+ end
496
+ end
497
+
498
+ # ln Gamma(z) for z >= 2*digits + 10 by Stirling's series, with an
499
+ # absolute error below 10**-digits:
500
+ # (z - 1/2) ln z - z + ln(2 pi)/2 + sum_k B_2k / (2k (2k-1) z**(2k-1)).
501
+ def lgamma_stirling(z, digits)
502
+ s = digits + ndigits(digits) + 10
503
+ log_z = log(z, s + [z.exponent, 0].max + ndigits(z.exponent.abs + 2) + 5)
504
+ main = fixed(times(minus(z, BigDecimal('0.5')), log_z), s) - fixed(z, s) + half_ln_2pi(s)
505
+ # z**-(2k-1) falls below 10**-s long before B_2k z**-(2k-1) does:
506
+ # carry the powers with as many more digits as the largest B_2k has.
507
+ extra = bernoulli_digits(stirling_terms(z, s)) + 5
508
+ scale = s + extra
509
+ unit = pow10(scale)
510
+ inverse = unit * unit / fixed(z, scale)
511
+ inverse2 = inverse * inverse / unit
512
+ power = inverse
513
+ sum = 0
514
+ k = 0
515
+ loop do
516
+ k += 1
517
+ b = bernoulli(k)[k - 1]
518
+ term = (b.numerator.abs * power) / (b.denominator * 2 * k * ((2 * k) - 1) * pow10(extra))
519
+ break if term.zero?
520
+
521
+ sum += b.negative? ? -term : term
522
+ power = power * inverse2 / unit
523
+ end
524
+ decimal(main + sum, s)
525
+ end
526
+
527
+ # How many Stirling terms bring |B_2k| / (2k (2k-1) z**(2k-1)) below
528
+ # 10**-s, with |B_2k| about 2 (2k)! / (2 pi)**2k; a few more for safety.
529
+ def stirling_terms(z, s)
530
+ log_z = z.to_f.finite? ? Math.log(z.to_f) : (z.exponent * Math.log(10))
531
+ limit = -(s + 5) * Math.log(10)
532
+ k = 1
533
+ k += 1 while k < 100_000 && log_bernoulli(k) - Math.log(2.0 * k * ((2 * k) - 1)) - (((2 * k) - 1) * log_z) > limit
534
+ k + 10
535
+ end
536
+
537
+ # ln |B_2k|, near enough.
538
+ def log_bernoulli(k) = Math.log(2) + Math.lgamma((2 * k) + 1)[0] - (2 * k * Math.log(2 * Math::PI))
539
+
540
+ def bernoulli_digits(k) = [(log_bernoulli(k) / Math.log(10)).ceil, 0].max
541
+
542
+ # ln Gamma(x) for a finite x >= 0.5, with an absolute error below
543
+ # 10**-digits: Stirling at z = x + m, less ln(x (x+1) ... (x+m-1)).
544
+ def lgamma_positive(x, digits)
545
+ start = (2 * digits) + 10
546
+ m = [start - x.floor, 0].max
547
+ return lgamma_stirling(x, digits) if m.zero?
548
+
549
+ z = plus(x, BigDecimal(m))
550
+ stirling = lgamma_stirling(z, digits + 2)
551
+ size = ndigits(((m + 1) * (Math.log([z.to_f, 2].max) + 1)).ceil)
552
+ w = digits + size + ndigits(m) + 10
553
+ product = x
554
+ (1...m).each { |i| product = product.mult(plus(x, BigDecimal(i)), w) }
555
+ s = digits + 5
556
+ decimal(fixed(stirling, s) - fixed(log(product, w), s), s)
557
+ end
558
+
559
+ # sin(pi x) for a finite x that is not an integer, to w digits, by
560
+ # reducing x to [-1/2, 1/2] exactly first.
561
+ def sin_pi(x, w)
562
+ n = x.round
563
+ f = minus(x, BigDecimal(n))
564
+ value = sin_cos(times(pi_decimal(w + 10 - [f.exponent, 0].min), f), w + 5, false)
565
+ n.odd? ? -value : value
566
+ end
567
+
568
+ def factorial(n)
569
+ return 1 if n < 2
570
+
571
+ product_range(2, n)
572
+ end
573
+
574
+ def product_range(low, high)
575
+ return low if low == high
576
+ return low * high if high == low + 1
577
+
578
+ mid = (low + high) / 2
579
+ product_range(low, mid) * product_range(mid + 1, high)
580
+ end
581
+ end
582
+ end