Coverage Report

Created: 2026-03-31 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.41.0/src/maths.rs
Line
Count
Source
1
use crate::prelude::*;
2
use num_traits::pow::Pow;
3
4
// Approximation of 1/ln(10) = 0.4342944819032518276511289189
5
const LN10_INVERSE: Decimal = Decimal::from_parts_raw(1763037029, 1670682625, 235431510, 1835008);
6
// PI / 8
7
const EIGHTH_PI: Decimal = Decimal::from_parts_raw(2822163429, 3244459792, 212882598, 1835008);
8
9
// Table representing {index}! — used in tests to verify factorial values.
10
#[cfg(test)]
11
const FACTORIAL: [Decimal; 28] = [
12
    Decimal::from_parts(1, 0, 0, false, 0),
13
    Decimal::from_parts(1, 0, 0, false, 0),
14
    Decimal::from_parts(2, 0, 0, false, 0),
15
    Decimal::from_parts(6, 0, 0, false, 0),
16
    Decimal::from_parts(24, 0, 0, false, 0),
17
    // 5!
18
    Decimal::from_parts(120, 0, 0, false, 0),
19
    Decimal::from_parts(720, 0, 0, false, 0),
20
    Decimal::from_parts(5040, 0, 0, false, 0),
21
    Decimal::from_parts(40320, 0, 0, false, 0),
22
    Decimal::from_parts(362880, 0, 0, false, 0),
23
    // 10!
24
    Decimal::from_parts(3628800, 0, 0, false, 0),
25
    Decimal::from_parts(39916800, 0, 0, false, 0),
26
    Decimal::from_parts(479001600, 0, 0, false, 0),
27
    Decimal::from_parts(1932053504, 1, 0, false, 0),
28
    Decimal::from_parts(1278945280, 20, 0, false, 0),
29
    // 15!
30
    Decimal::from_parts(2004310016, 304, 0, false, 0),
31
    Decimal::from_parts(2004189184, 4871, 0, false, 0),
32
    Decimal::from_parts(4006445056, 82814, 0, false, 0),
33
    Decimal::from_parts(3396534272, 1490668, 0, false, 0),
34
    Decimal::from_parts(109641728, 28322707, 0, false, 0),
35
    // 20!
36
    Decimal::from_parts(2192834560, 566454140, 0, false, 0),
37
    Decimal::from_parts(3099852800, 3305602358, 2, false, 0),
38
    Decimal::from_parts(3772252160, 4003775155, 60, false, 0),
39
    Decimal::from_parts(862453760, 1892515369, 1401, false, 0),
40
    Decimal::from_parts(3519021056, 2470695900, 33634, false, 0),
41
    // 25!
42
    Decimal::from_parts(2076180480, 1637855376, 840864, false, 0),
43
    Decimal::from_parts(2441084928, 3929534124, 21862473, false, 0),
44
    Decimal::from_parts(1484783616, 3018206259, 590286795, false, 0),
45
];
46
47
/// Trait exposing various mathematical operations that can be applied using a Decimal. This is only
48
/// present when the `maths` feature has been enabled, e.g. by adding the crate with
49
// `cargo add rust_decimal --features maths` and importing in your Rust file with `use rust_decimal::MathematicalOps;`
50
pub trait MathematicalOps {
51
    /// The estimated exponential function, e<sup>x</sup>. Stops calculating when it is within
52
    /// tolerance of roughly `0.0000002`.
53
    fn exp(&self) -> Decimal;
54
55
    /// The estimated exponential function, e<sup>x</sup>. Stops calculating when it is within
56
    /// tolerance of roughly `0.0000002`. Returns `None` on overflow.
57
    fn checked_exp(&self) -> Option<Decimal>;
58
59
    /// The estimated exponential function, e<sup>x</sup> using the `tolerance` provided as a hint
60
    /// as to when to stop calculating. A larger tolerance will cause the number to stop calculating
61
    /// sooner at the potential cost of a slightly less accurate result.
62
    fn exp_with_tolerance(&self, tolerance: Decimal) -> Decimal;
63
64
    /// The estimated exponential function, e<sup>x</sup> using the `tolerance` provided as a hint
65
    /// as to when to stop calculating. A larger tolerance will cause the number to stop calculating
66
    /// sooner at the potential cost of a slightly less accurate result.
67
    /// Returns `None` on overflow.
68
    fn checked_exp_with_tolerance(&self, tolerance: Decimal) -> Option<Decimal>;
69
70
    /// Raise self to the given integer exponent: x<sup>y</sup>
71
    fn powi(&self, exp: i64) -> Decimal;
72
73
    /// Raise self to the given integer exponent x<sup>y</sup> returning `None` on overflow.
74
    fn checked_powi(&self, exp: i64) -> Option<Decimal>;
75
76
    /// Raise self to the given unsigned integer exponent: x<sup>y</sup>
77
    fn powu(&self, exp: u64) -> Decimal;
78
79
    /// Raise self to the given unsigned integer exponent x<sup>y</sup> returning `None` on overflow.
80
    fn checked_powu(&self, exp: u64) -> Option<Decimal>;
81
82
    /// Raise self to the given floating point exponent: x<sup>y</sup>
83
    fn powf(&self, exp: f64) -> Decimal;
84
85
    /// Raise self to the given floating point exponent x<sup>y</sup> returning `None` on overflow.
86
    fn checked_powf(&self, exp: f64) -> Option<Decimal>;
87
88
    /// Raise self to the given Decimal exponent: x<sup>y</sup>. If `exp` is not whole then the approximation
89
    /// e<sup>y*ln(x)</sup> is used.
90
    fn powd(&self, exp: Decimal) -> Decimal;
91
92
    /// Raise self to the given Decimal exponent x<sup>y</sup> returning `None` on overflow.
93
    /// If `exp` is not whole then the approximation e<sup>y*ln(x)</sup> is used.
94
    fn checked_powd(&self, exp: Decimal) -> Option<Decimal>;
95
96
    /// The square root of a Decimal. Uses a standard Babylonian method.
97
    fn sqrt(&self) -> Option<Decimal>;
98
99
    /// Calculates the natural logarithm for a Decimal calculated using Taylor's series.
100
    fn ln(&self) -> Decimal;
101
102
    /// Calculates the checked natural logarithm for a Decimal calculated using Taylor's series.
103
    /// Returns `None` for negative numbers or zero.
104
    fn checked_ln(&self) -> Option<Decimal>;
105
106
    /// Calculates the base 10 logarithm of a specified Decimal number.
107
    fn log10(&self) -> Decimal;
108
109
    /// Calculates the checked base 10 logarithm of a specified Decimal number.
110
    /// Returns `None` for negative numbers or zero.
111
    fn checked_log10(&self) -> Option<Decimal>;
112
113
    /// Abramowitz Approximation of Error Function from [wikipedia](https://en.wikipedia.org/wiki/Error_function#Numerical_approximations)
114
    fn erf(&self) -> Decimal;
115
116
    /// The Cumulative distribution function for a Normal distribution
117
    fn norm_cdf(&self) -> Decimal;
118
119
    /// The Probability density function for a Normal distribution.
120
    fn norm_pdf(&self) -> Decimal;
121
122
    /// The Probability density function for a Normal distribution returning `None` on overflow.
123
    fn checked_norm_pdf(&self) -> Option<Decimal>;
124
125
    /// Computes the sine of a number (in radians).
126
    /// Panics upon overflow.
127
    fn sin(&self) -> Decimal;
128
129
    /// Computes the checked sine of a number (in radians).
130
    fn checked_sin(&self) -> Option<Decimal>;
131
132
    /// Computes the cosine of a number (in radians).
133
    /// Panics upon overflow.
134
    fn cos(&self) -> Decimal;
135
136
    /// Computes the checked cosine of a number (in radians).
137
    fn checked_cos(&self) -> Option<Decimal>;
138
139
    /// Computes the tangent of a number (in radians).
140
    /// Panics upon overflow or upon approaching a limit.
141
    fn tan(&self) -> Decimal;
142
143
    /// Computes the checked tangent of a number (in radians).
144
    /// Returns None on limit.
145
    fn checked_tan(&self) -> Option<Decimal>;
146
}
147
148
impl MathematicalOps for Decimal {
149
0
    fn exp(&self) -> Decimal {
150
0
        match self.checked_exp() {
151
0
            Some(d) => d,
152
            None => {
153
0
                if self.is_sign_negative() {
154
0
                    panic!("Exp underflowed")
155
                } else {
156
0
                    panic!("Exp overflowed")
157
                }
158
            }
159
        }
160
0
    }
161
162
0
    fn checked_exp(&self) -> Option<Decimal> {
163
0
        crate::ops::wide::exp_wide(self)
164
0
    }
165
166
0
    fn exp_with_tolerance(&self, tolerance: Decimal) -> Decimal {
167
0
        match self.checked_exp_with_tolerance(tolerance) {
168
0
            Some(d) => d,
169
            None => {
170
0
                if self.is_sign_negative() {
171
0
                    panic!("Exp underflowed")
172
                } else {
173
0
                    panic!("Exp overflowed")
174
                }
175
            }
176
        }
177
0
    }
178
179
0
    fn checked_exp_with_tolerance(&self, tolerance: Decimal) -> Option<Decimal> {
180
0
        if self.is_zero() {
181
0
            return Some(Decimal::ONE);
182
0
        }
183
0
        if self.is_sign_negative() {
184
0
            let mut flipped = *self;
185
0
            flipped.set_sign_positive(true);
186
0
            let exp = flipped.checked_exp_with_tolerance(tolerance)?;
187
0
            return Decimal::ONE.checked_div(exp);
188
0
        }
189
190
        // exp(x) = sum_i x^i / i!, let q_i := x^i/i!
191
        // Avoid computing x^i directly as it will quickly outgrow exp(x) for x > 1.
192
        // Instead we compute q_i = x*(x/2)*(x/3)*...*(x/i)
193
194
0
        let mut result = self.checked_add(Decimal::ONE)?;
195
0
        let mut term = *self;
196
197
0
        for i in 2..200u32 {
198
0
            let i_dec = Decimal::from_u32(i).unwrap();
199
0
            term = self.checked_mul(term.checked_div(i_dec)?)?;
200
0
            result = result.checked_add(term)?;
201
0
            if term <= tolerance {
202
0
                break;
203
0
            }
204
        }
205
206
0
        Some(result)
207
0
    }
208
209
0
    fn powi(&self, exp: i64) -> Decimal {
210
0
        match self.checked_powi(exp) {
211
0
            Some(result) => result,
212
0
            None => panic!("Pow overflowed"),
213
        }
214
0
    }
215
216
0
    fn checked_powi(&self, exp: i64) -> Option<Decimal> {
217
        // For negative exponents we change x^-y into 1 / x^y.
218
        // Otherwise, we calculate a standard unsigned exponent
219
0
        if exp >= 0 {
220
0
            return self.checked_powu(exp as u64);
221
0
        }
222
223
        // Get the unsigned exponent
224
0
        let exp = exp.unsigned_abs();
225
0
        let pow = self.checked_powu(exp)?;
226
0
        Decimal::ONE.checked_div(pow)
227
0
    }
228
229
0
    fn powu(&self, exp: u64) -> Decimal {
230
0
        match self.checked_powu(exp) {
231
0
            Some(result) => result,
232
0
            None => panic!("Pow overflowed"),
233
        }
234
0
    }
235
236
0
    fn checked_powu(&self, exp: u64) -> Option<Decimal> {
237
0
        crate::ops::wide::powu_wide(self, exp)
238
0
    }
239
240
0
    fn powf(&self, exp: f64) -> Decimal {
241
0
        match self.checked_powf(exp) {
242
0
            Some(result) => result,
243
0
            None => panic!("Pow overflowed"),
244
        }
245
0
    }
246
247
0
    fn checked_powf(&self, exp: f64) -> Option<Decimal> {
248
0
        let exp = Decimal::from_f64(exp)?;
249
0
        self.checked_powd(exp)
250
0
    }
251
252
0
    fn powd(&self, exp: Decimal) -> Decimal {
253
0
        match self.checked_powd(exp) {
254
0
            Some(result) => result,
255
0
            None => panic!("Pow overflowed"),
256
        }
257
0
    }
258
259
0
    fn checked_powd(&self, exp: Decimal) -> Option<Decimal> {
260
0
        if exp.is_zero() {
261
0
            return Some(Decimal::ONE);
262
0
        }
263
0
        if self.is_zero() {
264
0
            return Some(Decimal::ZERO);
265
0
        }
266
0
        if self.is_one() {
267
0
            return Some(Decimal::ONE);
268
0
        }
269
0
        if exp.is_one() {
270
0
            return Some(*self);
271
0
        }
272
273
        // If the scale is 0 then it's a trivial calculation
274
0
        let exp = exp.normalize();
275
0
        if exp.scale() == 0 {
276
0
            if exp.mid() != 0 || exp.hi() != 0 {
277
                // Exponent way too big
278
0
                return None;
279
0
            }
280
281
0
            return if exp.is_sign_negative() {
282
0
                self.checked_powi(-(exp.lo() as i64))
283
            } else {
284
0
                self.checked_powu(exp.lo() as u64)
285
            };
286
0
        }
287
288
        // We do some approximations since we've got a decimal exponent.
289
        // For positive bases: a^b = exp(b*ln(a))
290
0
        let negative = self.is_sign_negative();
291
0
        let e = self.abs().ln().checked_mul(exp)?;
292
0
        let mut result = e.checked_exp()?;
293
0
        result.set_sign_negative(negative);
294
0
        Some(result)
295
0
    }
296
297
0
    fn sqrt(&self) -> Option<Decimal> {
298
0
        if self.is_sign_negative() {
299
0
            return None;
300
0
        }
301
302
0
        if self.is_zero() {
303
0
            return Some(Decimal::ZERO);
304
0
        }
305
306
        // Start with an arbitrary number as the first guess
307
0
        let mut result = self / Decimal::TWO;
308
        // Too small to represent, so we start with self
309
        // Future iterations could actually avoid using a decimal altogether and use a buffered
310
        // vector, only combining back into a decimal on return
311
0
        if result.is_zero() {
312
0
            result = *self;
313
0
        }
314
0
        let mut last = result + Decimal::ONE;
315
316
        // Keep going while the difference is larger than the tolerance
317
0
        let mut circuit_breaker = 0;
318
0
        while last != result {
319
0
            circuit_breaker += 1;
320
0
            assert!(circuit_breaker < 1000, "geo mean circuit breaker");
321
322
0
            last = result;
323
0
            result = (result + self / result) / Decimal::TWO;
324
        }
325
326
0
        Some(result)
327
0
    }
328
329
    #[cfg(feature = "maths-nopanic")]
330
    fn ln(&self) -> Decimal {
331
        match self.checked_ln() {
332
            Some(result) => result,
333
            None => Decimal::ZERO,
334
        }
335
    }
336
337
    #[cfg(not(feature = "maths-nopanic"))]
338
0
    fn ln(&self) -> Decimal {
339
0
        match self.checked_ln() {
340
0
            Some(result) => result,
341
            None => {
342
0
                if self.is_sign_negative() {
343
0
                    panic!("Unable to calculate ln for negative numbers")
344
0
                } else if self.is_zero() {
345
0
                    panic!("Unable to calculate ln for zero")
346
                } else {
347
0
                    panic!("Calculation of ln failed for unknown reasons")
348
                }
349
            }
350
        }
351
0
    }
352
353
0
    fn checked_ln(&self) -> Option<Decimal> {
354
0
        crate::ops::wide::ln_wide(self)
355
0
    }
356
357
    #[cfg(feature = "maths-nopanic")]
358
    fn log10(&self) -> Decimal {
359
        match self.checked_log10() {
360
            Some(result) => result,
361
            None => Decimal::ZERO,
362
        }
363
    }
364
365
    #[cfg(not(feature = "maths-nopanic"))]
366
0
    fn log10(&self) -> Decimal {
367
0
        match self.checked_log10() {
368
0
            Some(result) => result,
369
            None => {
370
0
                if self.is_sign_negative() {
371
0
                    panic!("Unable to calculate log10 for negative numbers")
372
0
                } else if self.is_zero() {
373
0
                    panic!("Unable to calculate log10 for zero")
374
                } else {
375
0
                    panic!("Calculation of log10 failed for unknown reasons")
376
                }
377
            }
378
        }
379
0
    }
380
381
0
    fn checked_log10(&self) -> Option<Decimal> {
382
        use crate::ops::array::{div_by_u32, is_all_zero};
383
        // Early exits
384
0
        if self.is_sign_negative() || self.is_zero() {
385
0
            return None;
386
0
        }
387
0
        if self.is_one() {
388
0
            return Some(Decimal::ZERO);
389
0
        }
390
391
        // This uses a very basic method for calculating log10. We know the following is true:
392
        //   log10(n) = ln(n) / ln(10)
393
        // From this we can perform some small optimizations:
394
        //  1. ln(10) is a constant
395
        //  2. Multiplication is faster than division, so we can pre-calculate the constant 1/ln(10)
396
        // This allows us to then simplify log10(n) to:
397
        //   log10(n) = C * ln(n)
398
399
        // Before doing all of this however, we see if there are simple calculations to be made.
400
0
        let scale = self.scale();
401
0
        let mut working = self.mantissa_array3();
402
403
        // Check for scales less than 1 as an early exit
404
0
        if scale > 0 && working[2] == 0 && working[1] == 0 && working[0] == 1 {
405
0
            return Some(Decimal::from_parts(scale, 0, 0, true, 0));
406
0
        }
407
408
        // Loop for detecting bordering base 10 values
409
0
        let mut result = 0;
410
0
        let mut base10 = true;
411
0
        while !is_all_zero(&working) {
412
0
            let remainder = div_by_u32(&mut working, 10u32);
413
0
            if remainder != 0 {
414
0
                base10 = false;
415
0
                break;
416
0
            }
417
0
            result += 1;
418
0
            if working[2] == 0 && working[1] == 0 && working[0] == 1 {
419
0
                break;
420
0
            }
421
        }
422
0
        if base10 {
423
0
            return Some((result - scale as i32).into());
424
0
        }
425
426
0
        self.checked_ln().map(|result| LN10_INVERSE * result)
427
0
    }
428
429
0
    fn erf(&self) -> Decimal {
430
0
        if self.is_sign_positive() {
431
0
            let one = &Decimal::ONE;
432
433
0
            let xa1 = self * Decimal::from_parts(705230784, 0, 0, false, 10);
434
0
            let xa2 = self.powi(2) * Decimal::from_parts(422820123, 0, 0, false, 10);
435
0
            let xa3 = self.powi(3) * Decimal::from_parts(92705272, 0, 0, false, 10);
436
0
            let xa4 = self.powi(4) * Decimal::from_parts(1520143, 0, 0, false, 10);
437
0
            let xa5 = self.powi(5) * Decimal::from_parts(2765672, 0, 0, false, 10);
438
0
            let xa6 = self.powi(6) * Decimal::from_parts(430638, 0, 0, false, 10);
439
440
0
            let sum = one + xa1 + xa2 + xa3 + xa4 + xa5 + xa6;
441
0
            one - (one / sum.powi(16))
442
        } else {
443
0
            -self.abs().erf()
444
        }
445
0
    }
446
447
0
    fn norm_cdf(&self) -> Decimal {
448
0
        (Decimal::ONE + (self / Decimal::from_parts(2318911239, 3292722, 0, false, 16)).erf()) / Decimal::TWO
449
0
    }
450
451
0
    fn norm_pdf(&self) -> Decimal {
452
0
        match self.checked_norm_pdf() {
453
0
            Some(d) => d,
454
0
            None => panic!("Norm Pdf overflowed"),
455
        }
456
0
    }
457
458
0
    fn checked_norm_pdf(&self) -> Option<Decimal> {
459
0
        let sqrt2pi = Decimal::from_parts_raw(2133383024, 2079885984, 1358845910, 1835008);
460
0
        let factor = -self.checked_powi(2)?;
461
0
        let factor = factor.checked_div(Decimal::TWO)?;
462
0
        factor.checked_exp()?.checked_div(sqrt2pi)
463
0
    }
464
465
0
    fn sin(&self) -> Decimal {
466
0
        match self.checked_sin() {
467
0
            Some(x) => x,
468
0
            None => panic!("Sin overflowed"),
469
        }
470
0
    }
471
472
0
    fn checked_sin(&self) -> Option<Decimal> {
473
0
        crate::ops::wide::sin_wide(self)
474
0
    }
475
476
0
    fn cos(&self) -> Decimal {
477
0
        match self.checked_cos() {
478
0
            Some(x) => x,
479
0
            None => panic!("Cos overflowed"),
480
        }
481
0
    }
482
483
0
    fn checked_cos(&self) -> Option<Decimal> {
484
0
        crate::ops::wide::cos_wide(self)
485
0
    }
486
487
0
    fn tan(&self) -> Decimal {
488
0
        match self.checked_tan() {
489
0
            Some(x) => x,
490
0
            None => panic!("Tan overflowed"),
491
        }
492
0
    }
493
494
0
    fn checked_tan(&self) -> Option<Decimal> {
495
0
        if self.is_zero() {
496
0
            return Some(Decimal::ZERO);
497
0
        }
498
0
        if self.is_sign_negative() {
499
            // -Tan(-x)
500
0
            return (-self).checked_tan().map(|x| -x);
501
0
        }
502
0
        if self >= &Decimal::TWO_PI {
503
            // Reduce large numbers early - we can do this using rem to constrain to a range
504
0
            let adjusted = self.checked_rem(Decimal::TWO_PI)?;
505
0
            return adjusted.checked_tan();
506
0
        }
507
        // Reduce to 0 <= x <= PI
508
0
        if self >= &Decimal::PI {
509
            // Tan(x-π)
510
0
            return (self - Decimal::PI).checked_tan();
511
0
        }
512
        // Reduce to 0 <= x <= PI/2
513
0
        if self > &Decimal::HALF_PI {
514
            // We can use the symmetrical function inside the first quadrant
515
            // e.g. tan(x) = -tan((PI/2 - x) + PI/2)
516
0
            return ((Decimal::HALF_PI - self) + Decimal::HALF_PI).checked_tan().map(|x| -x);
517
0
        }
518
519
        // It has now been reduced to 0 <= x <= PI/2. If it is >= PI/4 we can make it even smaller
520
        // by calculating tan(PI/2 - x) and taking the reciprocal
521
0
        if self > &Decimal::QUARTER_PI {
522
0
            return match (Decimal::HALF_PI - self).checked_tan() {
523
0
                Some(x) => Decimal::ONE.checked_div(x),
524
0
                None => None,
525
            };
526
0
        }
527
528
        // Due the way that tan(x) sharply tends towards infinity, we try to optimize
529
        // the resulting accuracy by using Trigonometric identity when > PI/8. We do this by
530
        // replacing the angle with one that is half as big.
531
0
        if self > &EIGHTH_PI {
532
            // Work out tan(x/2)
533
0
            let tan_half = (self / Decimal::TWO).checked_tan()?;
534
            // Work out the dividend i.e. 2tan(x/2)
535
0
            let dividend = Decimal::TWO.checked_mul(tan_half)?;
536
537
            // Work out the divisor i.e. 1 - tan^2(x/2)
538
0
            let squared = tan_half.checked_mul(tan_half)?;
539
0
            let divisor = Decimal::ONE - squared;
540
            // Treat this as infinity
541
0
            if divisor.is_zero() {
542
0
                return None;
543
0
            }
544
0
            return dividend.checked_div(divisor);
545
0
        }
546
547
        // Do a polynomial approximation based upon the Maclaurin series.
548
        // This can be simplified to something like:
549
        //
550
        // ∑(n=1,3,5,7,9)(f(n)(0)/n!)x^n
551
        //
552
        // First few expansions (which we leverage):
553
        // (f'(0)/1!)x^1 + (f'''(0)/3!)x^3 + (f'''''(0)/5!)x^5 + (f'''''''/7!)x^7
554
        //
555
        // x + (1/3)x^3 + (2/15)x^5 + (17/315)x^7 + (62/2835)x^9 + (1382/155925)x^11
556
        //
557
        // (Generated by https://www.wolframalpha.com/widgets/view.jsp?id=fe1ad8d4f5dbb3cb866d0c89beb527a6)
558
        // The more terms, the better the accuracy. This generates accuracy within approx 10^-8 for angles
559
        // less than PI/8.
560
        const SERIES: [(Decimal, u64); 6] = [
561
            // 1 / 3
562
            (Decimal::from_parts_raw(89478485, 347537611, 180700362, 1835008), 3),
563
            // 2 / 15
564
            (Decimal::from_parts_raw(894784853, 3574988881, 72280144, 1835008), 5),
565
            // 17 / 315
566
            (Decimal::from_parts_raw(905437054, 3907911371, 2925624, 1769472), 7),
567
            // 62 / 2835
568
            (Decimal::from_parts_raw(3191872741, 2108928381, 11855473, 1835008), 9),
569
            // 1382 / 155925
570
            (Decimal::from_parts_raw(3482645539, 2612995122, 4804769, 1835008), 11),
571
            // 21844 / 6081075
572
            (Decimal::from_parts_raw(4189029078, 2192791200, 1947296, 1835008), 13),
573
        ];
574
0
        let mut result = *self;
575
0
        for (fraction, pow) in SERIES {
576
0
            result += fraction * self.powu(pow);
577
0
        }
578
0
        Some(result)
579
0
    }
580
}
581
582
impl Pow<Decimal> for Decimal {
583
    type Output = Decimal;
584
585
0
    fn pow(self, rhs: Decimal) -> Self::Output {
586
0
        MathematicalOps::powd(&self, rhs)
587
0
    }
588
}
589
590
impl Pow<u64> for Decimal {
591
    type Output = Decimal;
592
593
0
    fn pow(self, rhs: u64) -> Self::Output {
594
0
        MathematicalOps::powu(&self, rhs)
595
0
    }
596
}
597
598
impl Pow<i64> for Decimal {
599
    type Output = Decimal;
600
601
0
    fn pow(self, rhs: i64) -> Self::Output {
602
0
        MathematicalOps::powi(&self, rhs)
603
0
    }
604
}
605
606
impl Pow<f64> for Decimal {
607
    type Output = Decimal;
608
609
0
    fn pow(self, rhs: f64) -> Self::Output {
610
0
        MathematicalOps::powf(&self, rhs)
611
0
    }
612
}
613
614
#[cfg(test)]
615
mod test {
616
    use super::*;
617
618
    #[cfg(not(feature = "std"))]
619
    use alloc::string::ToString;
620
621
    #[test]
622
    fn test_factorials() {
623
        assert_eq!("1", FACTORIAL[0].to_string(), "0!");
624
        assert_eq!("1", FACTORIAL[1].to_string(), "1!");
625
        assert_eq!("2", FACTORIAL[2].to_string(), "2!");
626
        assert_eq!("6", FACTORIAL[3].to_string(), "3!");
627
        assert_eq!("24", FACTORIAL[4].to_string(), "4!");
628
        assert_eq!("120", FACTORIAL[5].to_string(), "5!");
629
        assert_eq!("720", FACTORIAL[6].to_string(), "6!");
630
        assert_eq!("5040", FACTORIAL[7].to_string(), "7!");
631
        assert_eq!("40320", FACTORIAL[8].to_string(), "8!");
632
        assert_eq!("362880", FACTORIAL[9].to_string(), "9!");
633
        assert_eq!("3628800", FACTORIAL[10].to_string(), "10!");
634
        assert_eq!("39916800", FACTORIAL[11].to_string(), "11!");
635
        assert_eq!("479001600", FACTORIAL[12].to_string(), "12!");
636
        assert_eq!("6227020800", FACTORIAL[13].to_string(), "13!");
637
        assert_eq!("87178291200", FACTORIAL[14].to_string(), "14!");
638
        assert_eq!("1307674368000", FACTORIAL[15].to_string(), "15!");
639
        assert_eq!("20922789888000", FACTORIAL[16].to_string(), "16!");
640
        assert_eq!("355687428096000", FACTORIAL[17].to_string(), "17!");
641
        assert_eq!("6402373705728000", FACTORIAL[18].to_string(), "18!");
642
        assert_eq!("121645100408832000", FACTORIAL[19].to_string(), "19!");
643
        assert_eq!("2432902008176640000", FACTORIAL[20].to_string(), "20!");
644
        assert_eq!("51090942171709440000", FACTORIAL[21].to_string(), "21!");
645
        assert_eq!("1124000727777607680000", FACTORIAL[22].to_string(), "22!");
646
        assert_eq!("25852016738884976640000", FACTORIAL[23].to_string(), "23!");
647
        assert_eq!("620448401733239439360000", FACTORIAL[24].to_string(), "24!");
648
        assert_eq!("15511210043330985984000000", FACTORIAL[25].to_string(), "25!");
649
        assert_eq!("403291461126605635584000000", FACTORIAL[26].to_string(), "26!");
650
        assert_eq!("10888869450418352160768000000", FACTORIAL[27].to_string(), "27!");
651
    }
652
}