rust_decimal/
maths.rs

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