number_diff/functions/
series_expansions.rs

1use std::sync::Arc;
2
3use crate::{
4    Elementary::{self, *},
5    Error, Factorial, Function,
6};
7
8#[derive(Debug, Clone)]
9/// [SeriesExpansion](crate::SeriesExpansion) is an abstraction of the series expansion created when using
10pub enum SeriesExpansion {
11    /// A Taylor series expansion centered around 0.
12    MacLaurin(Elementary),
13    /// An approximation of an analytic function centered around some value using a polynomial.
14    /// See [this article](https://en.wikipedia.org/wiki/Taylor_series) for further information.
15    Taylor(Elementary),
16    /// An approximation of a periodic function into a sum of trigonometric functions.
17    /// See [this article](https://en.wikipedia.org/wiki/Fourier_series) for further information.
18    Fourier(Elementary),
19}
20impl SeriesExpansion {
21    /// Returns a [Function](crate::Function) instance from the provided
22    /// [SeriesExpansion](crate::SeriesExpansion) instance, consuming it in the process.
23    pub fn get_function(self) -> Function {
24        match self {
25            Self::MacLaurin(elem) => Function::from(elem),
26            Self::Taylor(elem) => Function::from(elem),
27            Self::Fourier(elem) => Function::from(elem),
28        }
29    }
30
31    /// Returns a [Elementary](crate::Elementary) instance from the provided
32    /// [SeriesExpansion](crate::SeriesExpansion) instance, consuming it in the process.
33    pub fn get_elementary(self) -> Elementary {
34        match self {
35            Self::MacLaurin(elem) => elem,
36            Self::Taylor(elem) => elem,
37            Self::Fourier(elem) => elem,
38        }
39    }
40}
41
42impl Elementary {
43    pub fn expand_maclaurin(&self, order: u8) -> Result<SeriesExpansion, Error> {
44        let series = self.expand_taylor(order, 0.)?;
45
46        if let SeriesExpansion::Taylor(res) = series {
47            Ok(SeriesExpansion::MacLaurin(res))
48        } else {
49            unreachable!()
50        }
51    }
52
53    pub fn expand_taylor(&self, order: u8, centre: f64) -> Result<SeriesExpansion, Error> {
54        let mut terms: Vec<Elementary> = Vec::new();
55
56        let mut current_derivative = self.clone();
57
58        let first_term = current_derivative.clone().call()(centre);
59
60        terms.push(Con(first_term));
61
62        for i in 1..=order {
63            current_derivative = current_derivative.derivative_unsimplified();
64
65            let ith_term = Pow(Arc::new(X - centre), Arc::new(Con(i as f64)))
66                * current_derivative.clone().call()(centre)
67                / ((i as usize).factorial() as f64);
68
69            terms.push(ith_term);
70        }
71
72        let mut res = Con(0.);
73
74        for term in terms {
75            res += term;
76        }
77
78        res = res.simplify()?;
79
80        // TODO: check the result against lagrange_error_bound to make sure the maximum error is
81        // within the bound
82
83        Ok(SeriesExpansion::Taylor(res))
84    }
85}