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        // exp(x) = sum_i x^i / i!, let q_i := x^i/i!
185        // Avoid computing x^i directly as it will quickly outgrow exp(x) for x > 1.
186        // Instead we compute q_i = x*(x/2)*(x/3)*...*(x/i)
187
188        // First two terms are done directly: y = 1 + x
189        let mut result = self.checked_add(Decimal::ONE)?;
190
191        // q_1 = x
192        let mut term = *self;
193
194        // Note: For smaller x the loop will terminate early due to the tolerance check. Only
195        // for very large x it will run more iterations, for example: exp(66.5) runs up about 186
196        // iterations.
197        const ITERATION_COUNT: u32 = 200;
198
199        for i in 2..ITERATION_COUNT {
200            // SAFETY: `i` is always a valid Decimal (and it's trivial to construct it).
201            let i_dec = Decimal::from_u32(i).unwrap();
202
203            term = self.checked_mul(term.checked_div(i_dec)?)?;
204
205            result = result.checked_add(term)?;
206
207            // Note that term is positive
208            if term <= tolerance {
209                break;
210            }
211        }
212
213        Some(result)
214    }
215
216    fn powi(&self, exp: i64) -> Decimal {
217        match self.checked_powi(exp) {
218            Some(result) => result,
219            None => panic!("Pow overflowed"),
220        }
221    }
222
223    fn checked_powi(&self, exp: i64) -> Option<Decimal> {
224        // For negative exponents we change x^-y into 1 / x^y.
225        // Otherwise, we calculate a standard unsigned exponent
226        if exp >= 0 {
227            return self.checked_powu(exp as u64);
228        }
229
230        // Get the unsigned exponent
231        let exp = exp.unsigned_abs();
232        let pow = match self.checked_powu(exp) {
233            Some(v) => v,
234            None => return None,
235        };
236        Decimal::ONE.checked_div(pow)
237    }
238
239    fn powu(&self, exp: u64) -> Decimal {
240        match self.checked_powu(exp) {
241            Some(result) => result,
242            None => panic!("Pow overflowed"),
243        }
244    }
245
246    fn checked_powu(&self, exp: u64) -> Option<Decimal> {
247        if exp == 0 {
248            return Some(Decimal::ONE);
249        }
250        if self.is_zero() {
251            return Some(Decimal::ZERO);
252        }
253        if self.is_one() {
254            return Some(Decimal::ONE);
255        }
256
257        match exp {
258            0 => unreachable!(),
259            1 => Some(*self),
260            2 => self.checked_mul(*self),
261            // Do the exponentiation by multiplying squares:
262            //   y = Sum (for each 1 bit in binary representation) of (2 ^ bit)
263            //   x ^ y = Sum (for each 1 bit in y) of (x ^ (2 ^ bit))
264            // See: https://en.wikipedia.org/wiki/Exponentiation_by_squaring
265            _ => {
266                let mut product = Decimal::ONE;
267                let mut mask = exp;
268                let mut power = *self;
269
270                // Run through just enough 1 bits
271                for n in 0..(64 - exp.leading_zeros()) {
272                    if n > 0 {
273                        power = power.checked_mul(power)?;
274                        mask >>= 1;
275                    }
276                    if mask & 0x01 > 0 {
277                        match product.checked_mul(power) {
278                            Some(r) => product = r,
279                            None => return None,
280                        };
281                    }
282                }
283                product.normalize_assign();
284                Some(product)
285            }
286        }
287    }
288
289    fn powf(&self, exp: f64) -> Decimal {
290        match self.checked_powf(exp) {
291            Some(result) => result,
292            None => panic!("Pow overflowed"),
293        }
294    }
295
296    fn checked_powf(&self, exp: f64) -> Option<Decimal> {
297        let exp = match Decimal::from_f64(exp) {
298            Some(f) => f,
299            None => return None,
300        };
301        self.checked_powd(exp)
302    }
303
304    fn powd(&self, exp: Decimal) -> Decimal {
305        match self.checked_powd(exp) {
306            Some(result) => result,
307            None => panic!("Pow overflowed"),
308        }
309    }
310
311    fn checked_powd(&self, exp: Decimal) -> Option<Decimal> {
312        if exp.is_zero() {
313            return Some(Decimal::ONE);
314        }
315        if self.is_zero() {
316            return Some(Decimal::ZERO);
317        }
318        if self.is_one() {
319            return Some(Decimal::ONE);
320        }
321        if exp.is_one() {
322            return Some(*self);
323        }
324
325        // If the scale is 0 then it's a trivial calculation
326        let exp = exp.normalize();
327        if exp.scale() == 0 {
328            if exp.mid() != 0 || exp.hi() != 0 {
329                // Exponent way too big
330                return None;
331            }
332
333            return if exp.is_sign_negative() {
334                self.checked_powi(-(exp.lo() as i64))
335            } else {
336                self.checked_powu(exp.lo() as u64)
337            };
338        }
339
340        // We do some approximations since we've got a decimal exponent.
341        // For positive bases: a^b = exp(b*ln(a))
342        let negative = self.is_sign_negative();
343        let e = match self.abs().ln().checked_mul(exp) {
344            Some(e) => e,
345            None => return None,
346        };
347        let mut result = e.checked_exp()?;
348        result.set_sign_negative(negative);
349        Some(result)
350    }
351
352    fn sqrt(&self) -> Option<Decimal> {
353        if self.is_sign_negative() {
354            return None;
355        }
356
357        if self.is_zero() {
358            return Some(Decimal::ZERO);
359        }
360
361        // Start with an arbitrary number as the first guess
362        let mut result = self / Decimal::TWO;
363        // Too small to represent, so we start with self
364        // Future iterations could actually avoid using a decimal altogether and use a buffered
365        // vector, only combining back into a decimal on return
366        if result.is_zero() {
367            result = *self;
368        }
369        let mut last = result + Decimal::ONE;
370
371        // Keep going while the difference is larger than the tolerance
372        let mut circuit_breaker = 0;
373        while last != result {
374            circuit_breaker += 1;
375            assert!(circuit_breaker < 1000, "geo mean circuit breaker");
376
377            last = result;
378            result = (result + self / result) / Decimal::TWO;
379        }
380
381        Some(result)
382    }
383
384    #[cfg(feature = "maths-nopanic")]
385    fn ln(&self) -> Decimal {
386        match self.checked_ln() {
387            Some(result) => result,
388            None => Decimal::ZERO,
389        }
390    }
391
392    #[cfg(not(feature = "maths-nopanic"))]
393    fn ln(&self) -> Decimal {
394        match self.checked_ln() {
395            Some(result) => result,
396            None => {
397                if self.is_sign_negative() {
398                    panic!("Unable to calculate ln for negative numbers")
399                } else if self.is_zero() {
400                    panic!("Unable to calculate ln for zero")
401                } else {
402                    panic!("Calculation of ln failed for unknown reasons")
403                }
404            }
405        }
406    }
407
408    fn checked_ln(&self) -> Option<Decimal> {
409        if self.is_sign_negative() || self.is_zero() {
410            return None;
411        }
412        if self.is_one() {
413            return Some(Decimal::ZERO);
414        }
415
416        // Approximate using Taylor Series
417        let mut x = *self;
418        let mut count = 0;
419        while x >= Decimal::ONE {
420            x *= Decimal::E_INVERSE;
421            count += 1;
422        }
423        while x <= Decimal::E_INVERSE {
424            x *= Decimal::E;
425            count -= 1;
426        }
427        x -= Decimal::ONE;
428        if x.is_zero() {
429            return Some(Decimal::new(count, 0));
430        }
431        let mut result = Decimal::ZERO;
432        let mut iteration = 0;
433        let mut y = Decimal::ONE;
434        let mut last = Decimal::ONE;
435        while last != result && iteration < 100 {
436            iteration += 1;
437            last = result;
438            y *= -x;
439            result += y / Decimal::new(iteration, 0);
440        }
441        Some(Decimal::new(count, 0) - result)
442    }
443
444    #[cfg(feature = "maths-nopanic")]
445    fn log10(&self) -> Decimal {
446        match self.checked_log10() {
447            Some(result) => result,
448            None => Decimal::ZERO,
449        }
450    }
451
452    #[cfg(not(feature = "maths-nopanic"))]
453    fn log10(&self) -> Decimal {
454        match self.checked_log10() {
455            Some(result) => result,
456            None => {
457                if self.is_sign_negative() {
458                    panic!("Unable to calculate log10 for negative numbers")
459                } else if self.is_zero() {
460                    panic!("Unable to calculate log10 for zero")
461                } else {
462                    panic!("Calculation of log10 failed for unknown reasons")
463                }
464            }
465        }
466    }
467
468    fn checked_log10(&self) -> Option<Decimal> {
469        use crate::ops::array::{div_by_u32, is_all_zero};
470        // Early exits
471        if self.is_sign_negative() || self.is_zero() {
472            return None;
473        }
474        if self.is_one() {
475            return Some(Decimal::ZERO);
476        }
477
478        // This uses a very basic method for calculating log10. We know the following is true:
479        //   log10(n) = ln(n) / ln(10)
480        // From this we can perform some small optimizations:
481        //  1. ln(10) is a constant
482        //  2. Multiplication is faster than division, so we can pre-calculate the constant 1/ln(10)
483        // This allows us to then simplify log10(n) to:
484        //   log10(n) = C * ln(n)
485
486        // Before doing all of this however, we see if there are simple calculations to be made.
487        let scale = self.scale();
488        let mut working = self.mantissa_array3();
489
490        // Check for scales less than 1 as an early exit
491        if scale > 0 && working[2] == 0 && working[1] == 0 && working[0] == 1 {
492            return Some(Decimal::from_parts(scale, 0, 0, true, 0));
493        }
494
495        // Loop for detecting bordering base 10 values
496        let mut result = 0;
497        let mut base10 = true;
498        while !is_all_zero(&working) {
499            let remainder = div_by_u32(&mut working, 10u32);
500            if remainder != 0 {
501                base10 = false;
502                break;
503            }
504            result += 1;
505            if working[2] == 0 && working[1] == 0 && working[0] == 1 {
506                break;
507            }
508        }
509        if base10 {
510            return Some((result - scale as i32).into());
511        }
512
513        self.checked_ln().map(|result| LN10_INVERSE * result)
514    }
515
516    fn erf(&self) -> Decimal {
517        if self.is_sign_positive() {
518            let one = &Decimal::ONE;
519
520            let xa1 = self * Decimal::from_parts(705230784, 0, 0, false, 10);
521            let xa2 = self.powi(2) * Decimal::from_parts(422820123, 0, 0, false, 10);
522            let xa3 = self.powi(3) * Decimal::from_parts(92705272, 0, 0, false, 10);
523            let xa4 = self.powi(4) * Decimal::from_parts(1520143, 0, 0, false, 10);
524            let xa5 = self.powi(5) * Decimal::from_parts(2765672, 0, 0, false, 10);
525            let xa6 = self.powi(6) * Decimal::from_parts(430638, 0, 0, false, 10);
526
527            let sum = one + xa1 + xa2 + xa3 + xa4 + xa5 + xa6;
528            one - (one / sum.powi(16))
529        } else {
530            -self.abs().erf()
531        }
532    }
533
534    fn norm_cdf(&self) -> Decimal {
535        (Decimal::ONE + (self / Decimal::from_parts(2318911239, 3292722, 0, false, 16)).erf()) / Decimal::TWO
536    }
537
538    fn norm_pdf(&self) -> Decimal {
539        match self.checked_norm_pdf() {
540            Some(d) => d,
541            None => panic!("Norm Pdf overflowed"),
542        }
543    }
544
545    fn checked_norm_pdf(&self) -> Option<Decimal> {
546        let sqrt2pi = Decimal::from_parts_raw(2133383024, 2079885984, 1358845910, 1835008);
547        let factor = -self.checked_powi(2)?;
548        let factor = factor.checked_div(Decimal::TWO)?;
549        factor.checked_exp()?.checked_div(sqrt2pi)
550    }
551
552    fn sin(&self) -> Decimal {
553        match self.checked_sin() {
554            Some(x) => x,
555            None => panic!("Sin overflowed"),
556        }
557    }
558
559    fn checked_sin(&self) -> Option<Decimal> {
560        if self.is_zero() {
561            return Some(Decimal::ZERO);
562        }
563        if self.is_sign_negative() {
564            // -Sin(-x)
565            return (-self).checked_sin().map(|x| -x);
566        }
567        if self >= &Decimal::TWO_PI {
568            // Reduce large numbers early - we can do this using rem to constrain to a range
569            let adjusted = self.checked_rem(Decimal::TWO_PI)?;
570            return adjusted.checked_sin();
571        }
572        if self >= &Decimal::PI {
573            // -Sin(x-π)
574            return (self - Decimal::PI).checked_sin().map(|x| -x);
575        }
576        if self > &Decimal::QUARTER_PI {
577            // Cos(π2-x)
578            return (Decimal::HALF_PI - self).checked_cos();
579        }
580
581        // Taylor series:
582        // ∑(n=0 to ∞) : ((−1)^n / (2n + 1)!) * x^(2n + 1) , x∈R
583        // First few expansions:
584        // x^1/1! - x^3/3! + x^5/5! - x^7/7! + x^9/9!
585        let mut result = Decimal::ZERO;
586        for n in 0..TRIG_SERIES_UPPER_BOUND {
587            let x = 2 * n + 1;
588            let element = self.checked_powi(x as i64)?.checked_div(FACTORIAL[x])?;
589            if n & 0x1 == 0 {
590                result += element;
591            } else {
592                result -= element;
593            }
594        }
595        Some(result)
596    }
597
598    fn cos(&self) -> Decimal {
599        match self.checked_cos() {
600            Some(x) => x,
601            None => panic!("Cos overflowed"),
602        }
603    }
604
605    fn checked_cos(&self) -> Option<Decimal> {
606        if self.is_zero() {
607            return Some(Decimal::ONE);
608        }
609        if self.is_sign_negative() {
610            // Cos(-x)
611            return (-self).checked_cos();
612        }
613        if self >= &Decimal::TWO_PI {
614            // Reduce large numbers early - we can do this using rem to constrain to a range
615            let adjusted = self.checked_rem(Decimal::TWO_PI)?;
616            return adjusted.checked_cos();
617        }
618        if self >= &Decimal::PI {
619            // -Cos(x-π)
620            return (self - Decimal::PI).checked_cos().map(|x| -x);
621        }
622        if self > &Decimal::QUARTER_PI {
623            // Sin(π2-x)
624            return (Decimal::HALF_PI - self).checked_sin();
625        }
626
627        // Taylor series:
628        // ∑(n=0 to ∞) : ((−1)^n / (2n)!) * x^(2n) , x∈R
629        // First few expansions:
630        // x^0/0! - x^2/2! + x^4/4! - x^6/6! + x^8/8!
631        let mut result = Decimal::ZERO;
632        for n in 0..TRIG_SERIES_UPPER_BOUND {
633            let x = 2 * n;
634            let element = self.checked_powi(x as i64)?.checked_div(FACTORIAL[x])?;
635            if n & 0x1 == 0 {
636                result += element;
637            } else {
638                result -= element;
639            }
640        }
641        Some(result)
642    }
643
644    fn tan(&self) -> Decimal {
645        match self.checked_tan() {
646            Some(x) => x,
647            None => panic!("Tan overflowed"),
648        }
649    }
650
651    fn checked_tan(&self) -> Option<Decimal> {
652        if self.is_zero() {
653            return Some(Decimal::ZERO);
654        }
655        if self.is_sign_negative() {
656            // -Tan(-x)
657            return (-self).checked_tan().map(|x| -x);
658        }
659        if self >= &Decimal::TWO_PI {
660            // Reduce large numbers early - we can do this using rem to constrain to a range
661            let adjusted = self.checked_rem(Decimal::TWO_PI)?;
662            return adjusted.checked_tan();
663        }
664        // Reduce to 0 <= x <= PI
665        if self >= &Decimal::PI {
666            // Tan(x-π)
667            return (self - Decimal::PI).checked_tan();
668        }
669        // Reduce to 0 <= x <= PI/2
670        if self > &Decimal::HALF_PI {
671            // We can use the symmetrical function inside the first quadrant
672            // e.g. tan(x) = -tan((PI/2 - x) + PI/2)
673            return ((Decimal::HALF_PI - self) + Decimal::HALF_PI).checked_tan().map(|x| -x);
674        }
675
676        // It has now been reduced to 0 <= x <= PI/2. If it is >= PI/4 we can make it even smaller
677        // by calculating tan(PI/2 - x) and taking the reciprocal
678        if self > &Decimal::QUARTER_PI {
679            return match (Decimal::HALF_PI - self).checked_tan() {
680                Some(x) => Decimal::ONE.checked_div(x),
681                None => None,
682            };
683        }
684
685        // Due the way that tan(x) sharply tends towards infinity, we try to optimize
686        // the resulting accuracy by using Trigonometric identity when > PI/8. We do this by
687        // replacing the angle with one that is half as big.
688        if self > &EIGHTH_PI {
689            // Work out tan(x/2)
690            let tan_half = (self / Decimal::TWO).checked_tan()?;
691            // Work out the dividend i.e. 2tan(x/2)
692            let dividend = Decimal::TWO.checked_mul(tan_half)?;
693
694            // Work out the divisor i.e. 1 - tan^2(x/2)
695            let squared = tan_half.checked_mul(tan_half)?;
696            let divisor = Decimal::ONE - squared;
697            // Treat this as infinity
698            if divisor.is_zero() {
699                return None;
700            }
701            return dividend.checked_div(divisor);
702        }
703
704        // Do a polynomial approximation based upon the Maclaurin series.
705        // This can be simplified to something like:
706        //
707        // ∑(n=1,3,5,7,9)(f(n)(0)/n!)x^n
708        //
709        // First few expansions (which we leverage):
710        // (f'(0)/1!)x^1 + (f'''(0)/3!)x^3 + (f'''''(0)/5!)x^5 + (f'''''''/7!)x^7
711        //
712        // x + (1/3)x^3 + (2/15)x^5 + (17/315)x^7 + (62/2835)x^9 + (1382/155925)x^11
713        //
714        // (Generated by https://www.wolframalpha.com/widgets/view.jsp?id=fe1ad8d4f5dbb3cb866d0c89beb527a6)
715        // The more terms, the better the accuracy. This generates accuracy within approx 10^-8 for angles
716        // less than PI/8.
717        const SERIES: [(Decimal, u64); 6] = [
718            // 1 / 3
719            (Decimal::from_parts_raw(89478485, 347537611, 180700362, 1835008), 3),
720            // 2 / 15
721            (Decimal::from_parts_raw(894784853, 3574988881, 72280144, 1835008), 5),
722            // 17 / 315
723            (Decimal::from_parts_raw(905437054, 3907911371, 2925624, 1769472), 7),
724            // 62 / 2835
725            (Decimal::from_parts_raw(3191872741, 2108928381, 11855473, 1835008), 9),
726            // 1382 / 155925
727            (Decimal::from_parts_raw(3482645539, 2612995122, 4804769, 1835008), 11),
728            // 21844 / 6081075
729            (Decimal::from_parts_raw(4189029078, 2192791200, 1947296, 1835008), 13),
730        ];
731        let mut result = *self;
732        for (fraction, pow) in SERIES {
733            result += fraction * self.powu(pow);
734        }
735        Some(result)
736    }
737}
738
739impl Pow<Decimal> for Decimal {
740    type Output = Decimal;
741
742    fn pow(self, rhs: Decimal) -> Self::Output {
743        MathematicalOps::powd(&self, rhs)
744    }
745}
746
747impl Pow<u64> for Decimal {
748    type Output = Decimal;
749
750    fn pow(self, rhs: u64) -> Self::Output {
751        MathematicalOps::powu(&self, rhs)
752    }
753}
754
755impl Pow<i64> for Decimal {
756    type Output = Decimal;
757
758    fn pow(self, rhs: i64) -> Self::Output {
759        MathematicalOps::powi(&self, rhs)
760    }
761}
762
763impl Pow<f64> for Decimal {
764    type Output = Decimal;
765
766    fn pow(self, rhs: f64) -> Self::Output {
767        MathematicalOps::powf(&self, rhs)
768    }
769}
770
771#[cfg(test)]
772mod test {
773    use super::*;
774
775    #[cfg(not(feature = "std"))]
776    use alloc::string::ToString;
777
778    #[test]
779    fn test_factorials() {
780        assert_eq!("1", FACTORIAL[0].to_string(), "0!");
781        assert_eq!("1", FACTORIAL[1].to_string(), "1!");
782        assert_eq!("2", FACTORIAL[2].to_string(), "2!");
783        assert_eq!("6", FACTORIAL[3].to_string(), "3!");
784        assert_eq!("24", FACTORIAL[4].to_string(), "4!");
785        assert_eq!("120", FACTORIAL[5].to_string(), "5!");
786        assert_eq!("720", FACTORIAL[6].to_string(), "6!");
787        assert_eq!("5040", FACTORIAL[7].to_string(), "7!");
788        assert_eq!("40320", FACTORIAL[8].to_string(), "8!");
789        assert_eq!("362880", FACTORIAL[9].to_string(), "9!");
790        assert_eq!("3628800", FACTORIAL[10].to_string(), "10!");
791        assert_eq!("39916800", FACTORIAL[11].to_string(), "11!");
792        assert_eq!("479001600", FACTORIAL[12].to_string(), "12!");
793        assert_eq!("6227020800", FACTORIAL[13].to_string(), "13!");
794        assert_eq!("87178291200", FACTORIAL[14].to_string(), "14!");
795        assert_eq!("1307674368000", FACTORIAL[15].to_string(), "15!");
796        assert_eq!("20922789888000", FACTORIAL[16].to_string(), "16!");
797        assert_eq!("355687428096000", FACTORIAL[17].to_string(), "17!");
798        assert_eq!("6402373705728000", FACTORIAL[18].to_string(), "18!");
799        assert_eq!("121645100408832000", FACTORIAL[19].to_string(), "19!");
800        assert_eq!("2432902008176640000", FACTORIAL[20].to_string(), "20!");
801        assert_eq!("51090942171709440000", FACTORIAL[21].to_string(), "21!");
802        assert_eq!("1124000727777607680000", FACTORIAL[22].to_string(), "22!");
803        assert_eq!("25852016738884976640000", FACTORIAL[23].to_string(), "23!");
804        assert_eq!("620448401733239439360000", FACTORIAL[24].to_string(), "24!");
805        assert_eq!("15511210043330985984000000", FACTORIAL[25].to_string(), "25!");
806        assert_eq!("403291461126605635584000000", FACTORIAL[26].to_string(), "26!");
807        assert_eq!("10888869450418352160768000000", FACTORIAL[27].to_string(), "27!");
808    }
809}