mathhook_core/calculus/derivatives/
basic.rs

1//! Basic differentiation rules for constants, symbols, and sums
2
3use crate::calculus::derivatives::Derivative;
4use crate::core::{Expression, Symbol};
5use crate::simplify::Simplify;
6
7/// Basic derivative operations
8pub struct BasicDerivatives;
9
10impl BasicDerivatives {
11    /// Handle derivative of calculus expressions
12    ///
13    /// # Examples
14    ///
15    /// ```rust
16    /// use mathhook_core::{Expression, symbol};
17    /// use mathhook_core::calculus::derivatives::Derivative;
18    ///
19    /// let x = symbol!(x);
20    /// let expr = Expression::derivative(Expression::symbol(x.clone()), x.clone(), 1);
21    /// let second = expr.derivative(x);
22    /// ```
23    pub fn handle_calculus(
24        expr: &Expression,
25        data: &crate::core::expression::CalculusData,
26        variable: Symbol,
27    ) -> Expression {
28        match data {
29            crate::core::expression::CalculusData::Derivative {
30                variable: var,
31                order,
32                ..
33            } => {
34                if *var == variable {
35                    Expression::derivative(expr.clone(), variable, order + 1)
36                } else {
37                    Expression::integer(0) // d/dx[f(y)] = 0
38                }
39            }
40            _ => Expression::derivative(expr.clone(), variable, 1),
41        }
42    }
43
44    /// Handle derivative of symbol expressions
45    ///
46    /// # Examples
47    ///
48    /// ```rust
49    /// use mathhook_core::{Expression, symbol};
50    /// use mathhook_core::calculus::derivatives::Derivative;
51    ///
52    /// let x = symbol!(x);
53    /// let y = symbol!(y);
54    /// let expr = Expression::symbol(x.clone());
55    /// let dx = expr.derivative(x);
56    /// let dy = expr.derivative(y);
57    /// ```
58    pub fn handle_symbol(sym: &Symbol, variable: &Symbol) -> Expression {
59        if sym == variable {
60            Expression::integer(1) // dx/dx = 1
61        } else {
62            Expression::integer(0) // dy/dx = 0
63        }
64    }
65
66    /// Handle derivative of sum expressions using linearity
67    ///
68    /// # Examples
69    ///
70    /// ```rust
71    /// use mathhook_core::{Expression};
72    /// use mathhook_core::symbol;
73    /// use mathhook_core::calculus::derivatives::Derivative;
74    ///
75    /// let x = symbol!(x);
76    /// let expr = Expression::add(vec![
77    ///     Expression::symbol(x.clone()),
78    ///     Expression::integer(5)
79    /// ]);
80    /// let result = expr.derivative(x);
81    /// ```
82    pub fn handle_sum(terms: &[Expression], variable: &Symbol) -> Expression {
83        let mut derivative_terms = Vec::with_capacity(terms.len());
84
85        for term in terms {
86            derivative_terms.push(term.derivative(variable.clone()));
87        }
88
89        Expression::add(derivative_terms).simplify() // d/dx[f + g] = f' + g'
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use crate::symbol;
97    use crate::MathConstant;
98
99    #[test]
100    fn test_basic_constant_derivatives() {
101        let x = symbol!(x);
102        let y = symbol!(y);
103
104        assert_eq!(
105            Expression::integer(2).derivative(x.clone()),
106            Expression::integer(0) // d/dx[2] = 0
107        );
108        assert_eq!(
109            Expression::symbol(x.clone()).derivative(x.clone()),
110            Expression::integer(1) // dx/dx = 1
111        );
112        assert_eq!(
113            Expression::symbol(x.clone()).derivative(y.clone()),
114            Expression::integer(0) // dx/dy = 0
115        );
116        assert_eq!(
117            Expression::integer(-1).derivative(x.clone()),
118            Expression::integer(0) // d/dx[-1] = 0
119        );
120        assert_eq!(
121            Expression::constant(MathConstant::Pi).derivative(x.clone()),
122            Expression::integer(0) // d/dx[π] = 0
123        );
124    }
125
126    #[test]
127    fn test_sum_linearity() {
128        let x = symbol!(x);
129
130        // d/dx[x + 5] = 1 + 0 = 1
131        let sum = Expression::add(vec![Expression::symbol(x.clone()), Expression::integer(5)]);
132        let result = sum.derivative(x.clone()).simplify();
133        assert_eq!(result, Expression::integer(1));
134
135        // d/dx[2x + 3x] = 2 + 3 = 5
136        let linear_combo = Expression::add(vec![
137            Expression::mul(vec![Expression::integer(2), Expression::symbol(x.clone())]),
138            Expression::mul(vec![Expression::integer(3), Expression::symbol(x.clone())]),
139        ]);
140        let linear_result = linear_combo.derivative(x.clone()).simplify();
141        assert_eq!(linear_result, Expression::integer(5));
142    }
143
144    #[test]
145    fn test_multiple_variables() {
146        let x = symbol!(x);
147        let y = symbol!(y);
148
149        // f(x,y) = xy + x² + y
150        let expr = Expression::add(vec![
151            Expression::mul(vec![
152                Expression::symbol(x.clone()),
153                Expression::symbol(y.clone()),
154            ]),
155            Expression::pow(Expression::symbol(x.clone()), Expression::integer(2)),
156            Expression::symbol(y.clone()),
157        ]);
158
159        // ∂f/∂x = y + 2x
160        let dx = expr.derivative(x.clone()).simplify();
161        let expected_dx = Expression::add(vec![
162            Expression::symbol(y.clone()),
163            Expression::mul(vec![Expression::integer(2), Expression::symbol(x.clone())]),
164        ])
165        .simplify();
166
167        // ∂f/∂y = x + 1
168        let dy = expr.derivative(y.clone()).simplify();
169        let expected_dy =
170            Expression::add(vec![Expression::symbol(x.clone()), Expression::integer(1)]).simplify();
171
172        assert_eq!(dx, expected_dx);
173        assert_eq!(dy, expected_dy);
174    }
175
176    #[test]
177    fn test_special_constants() {
178        let x = symbol!(x);
179
180        // d/dx[π] = 0
181        let pi_derivative = Expression::constant(MathConstant::Pi).derivative(x.clone());
182        assert_eq!(pi_derivative, Expression::integer(0));
183
184        // d/dx[e] = 0
185        let e_derivative = Expression::constant(MathConstant::E).derivative(x.clone());
186        assert_eq!(e_derivative, Expression::integer(0));
187
188        // d/dx[i] = 0
189        let i_derivative = Expression::constant(MathConstant::I).derivative(x.clone());
190        assert_eq!(i_derivative, Expression::integer(0));
191    }
192
193    #[test]
194    fn test_nested_sums() {
195        let x = symbol!(x);
196
197        // d/dx[x + (2x + 3)] = d/dx[3x + 3] = 3
198        let nested = Expression::add(vec![
199            Expression::symbol(x.clone()),
200            Expression::add(vec![
201                Expression::mul(vec![Expression::integer(2), Expression::symbol(x.clone())]),
202                Expression::integer(3),
203            ]),
204        ]);
205
206        let result = nested.derivative(x.clone()).simplify();
207        assert_eq!(result, Expression::integer(3));
208    }
209
210    #[test]
211    fn test_zero_and_negative_constants() {
212        let x = symbol!(x);
213
214        // d/dx[0] = 0
215        assert_eq!(
216            Expression::integer(0).derivative(x.clone()),
217            Expression::integer(0)
218        );
219
220        // d/dx[-42] = 0
221        assert_eq!(
222            Expression::integer(-42).derivative(x.clone()),
223            Expression::integer(0)
224        );
225
226        // d/dx[3.14] = 0
227        assert_eq!(
228            Expression::float(std::f64::consts::PI).derivative(x.clone()),
229            Expression::integer(0)
230        );
231    }
232}