mathhook_core/calculus/derivatives/educational/
basic_rules.rs

1//! Basic differentiation rules: constant, variable, sum, and power rules
2
3use crate::calculus::derivatives::Derivative;
4use crate::core::{Expression, Number, Symbol};
5use crate::educational::message_registry::{MessageBuilder, MessageCategory, MessageType};
6use crate::educational::step_by_step::{Step, StepByStepExplanation};
7use crate::formatter::latex::LaTeXFormatter;
8use crate::simplify::Simplify;
9
10use super::format_expr;
11
12/// Explain constant derivative (2+ steps)
13pub fn explain_constant_derivative(expr: &Expression, variable: &Symbol) -> StepByStepExplanation {
14    let mut steps = Vec::new();
15
16    steps.push(Step {
17        title: "Identify Constant".to_owned(),
18        description: format!(
19            "{} is a constant (does not depend on {})",
20            format_expr(expr),
21            variable.name()
22        ),
23        expression: expr.clone(),
24        rule_applied: "Identification".to_owned(),
25        latex: Some(expr.to_latex(None).unwrap_or_else(|_| "c".to_owned())),
26    });
27
28    if let Some(step) = MessageBuilder::new(
29        MessageCategory::Calculus,
30        MessageType::DerivativeConstant,
31        0,
32    )
33    .with_substitution("constant", format_expr(expr))
34    .build()
35    {
36        steps.push(step);
37    }
38
39    let result = Expression::integer(0);
40    steps.push(Step {
41        title: "Result".to_owned(),
42        description: "0".to_owned(),
43        expression: result.clone(),
44        rule_applied: "Constant Rule".to_owned(),
45        latex: Some("0".to_owned()),
46    });
47
48    let step_count = steps.len();
49    StepByStepExplanation {
50        initial_expression: expr.clone(),
51        final_expression: result,
52        steps,
53        total_steps: step_count,
54        rules_used: vec!["Constant Rule".to_owned()],
55    }
56}
57
58/// Explain variable derivative (2+ steps)
59pub fn explain_variable_derivative(sym: &Symbol, variable: &Symbol) -> StepByStepExplanation {
60    let mut steps = Vec::new();
61    let expr = Expression::symbol(sym.clone());
62
63    if sym == variable {
64        steps.push(Step {
65            title: "Differentiate Variable".to_owned(),
66            description: format!("Differentiating {} with respect to itself", variable.name()),
67            expression: expr.clone(),
68            rule_applied: "Identification".to_owned(),
69            latex: Some(variable.name().to_owned()),
70        });
71
72        if let Some(step) = MessageBuilder::new(
73            MessageCategory::Calculus,
74            MessageType::DerivativeVariable,
75            0,
76        )
77        .with_substitution("variable", variable.name())
78        .build()
79        {
80            steps.push(step);
81        }
82
83        let result = Expression::integer(1);
84        steps.push(Step {
85            title: "Result".to_owned(),
86            description: "1".to_owned(),
87            expression: result.clone(),
88            rule_applied: "Variable Rule".to_owned(),
89            latex: Some("1".to_owned()),
90        });
91
92        let step_count = steps.len();
93        StepByStepExplanation {
94            initial_expression: expr,
95            final_expression: result,
96            steps,
97            total_steps: step_count,
98            rules_used: vec!["Variable Rule".to_owned()],
99        }
100    } else {
101        steps.push(Step {
102            title: "Different Variable".to_owned(),
103            description: format!(
104                "{} does not depend on {} (treating as constant)",
105                sym.name(),
106                variable.name()
107            ),
108            expression: expr.clone(),
109            rule_applied: "Identification".to_owned(),
110            latex: Some(sym.name().to_owned()),
111        });
112
113        let result = Expression::integer(0);
114        steps.push(Step {
115            title: "Result".to_owned(),
116            description: "0".to_owned(),
117            expression: result.clone(),
118            rule_applied: "Constant Rule".to_owned(),
119            latex: Some("0".to_owned()),
120        });
121
122        let step_count = steps.len();
123        StepByStepExplanation {
124            initial_expression: expr,
125            final_expression: result,
126            steps,
127            total_steps: step_count,
128            rules_used: vec!["Constant Rule".to_owned()],
129        }
130    }
131}
132
133/// Explain sum/difference rule (4+ steps)
134pub fn explain_sum_derivative(terms: &[Expression], variable: &Symbol) -> StepByStepExplanation {
135    let mut steps = Vec::new();
136    let expr = Expression::add(terms.to_vec());
137
138    steps.push(Step {
139        title: "Identify Sum".to_owned(),
140        description: format!("Function is a sum of {} terms", terms.len()),
141        expression: expr.clone(),
142        rule_applied: "Identification".to_owned(),
143        latex: Some(expr.to_latex(None).unwrap_or_else(|_| "sum".to_owned())),
144    });
145
146    steps.push(Step {
147        title: "Apply Sum Rule".to_owned(),
148        description:
149            "d/dx[f + g + h + ...] = f' + g' + h' + ...\nDifferentiate each term separately"
150                .to_owned(),
151        expression: expr.clone(),
152        rule_applied: "Sum Rule".to_owned(),
153        latex: Some(expr.to_latex(None).unwrap_or_else(|_| "sum".to_owned())),
154    });
155
156    let mut derivative_terms = Vec::new();
157    for (i, term) in terms.iter().enumerate() {
158        let term_derivative = term.derivative(variable.clone());
159        derivative_terms.push(term_derivative.clone());
160
161        steps.push(Step {
162            title: format!("Differentiate Term {}", i + 1),
163            description: format!(
164                "d/d{}({}) = {}",
165                variable.name(),
166                format_expr(term),
167                format_expr(&term_derivative)
168            ),
169            expression: term_derivative.clone(),
170            rule_applied: format!("Term {} Derivative", i + 1),
171            latex: Some(
172                term_derivative
173                    .to_latex(None)
174                    .unwrap_or_else(|_| "term".to_owned()),
175            ),
176        });
177    }
178
179    let result = Expression::add(derivative_terms).simplify();
180    steps.push(Step {
181        title: "Combine Results".to_owned(),
182        description: format!("Add all derivatives: {}", format_expr(&result)),
183        expression: result.clone(),
184        rule_applied: "Combination".to_owned(),
185        latex: Some(
186            result
187                .to_latex(None)
188                .unwrap_or_else(|_| "result".to_owned()),
189        ),
190    });
191
192    let step_count = steps.len();
193    StepByStepExplanation {
194        initial_expression: expr,
195        final_expression: result,
196        steps,
197        total_steps: step_count,
198        rules_used: vec!["Sum Rule".to_owned()],
199    }
200}
201
202/// Explain power rule (4+ steps)
203pub fn explain_power_rule(
204    base: &Expression,
205    exp: &Expression,
206    _variable: &Symbol,
207) -> StepByStepExplanation {
208    let mut steps = Vec::new();
209    let expr = Expression::pow(base.clone(), exp.clone());
210
211    steps.push(Step {
212        title: "Identify Power Function".to_owned(),
213        description: format!(
214            "Function is a power: {}^{}",
215            format_expr(base),
216            format_expr(exp)
217        ),
218        expression: expr.clone(),
219        rule_applied: "Identification".to_owned(),
220        latex: Some(expr.to_latex(None).unwrap_or_else(|_| "x^n".to_owned())),
221    });
222
223    let exp_str = if let Expression::Number(Number::Integer(n)) = exp {
224        format!("n = {}", n)
225    } else {
226        format!("n = {}", format_expr(exp))
227    };
228
229    if let Some(step) = MessageBuilder::new(
230        MessageCategory::Calculus,
231        MessageType::DerivativePowerRule,
232        0,
233    )
234    .with_substitution("expression", format_expr(&expr))
235    .with_substitution("exponent", &exp_str)
236    .build()
237    {
238        steps.push(step);
239    }
240
241    steps.push(Step {
242        title: "Apply Power Rule".to_owned(),
243        description: "d/dx(x^n) = n*x^(n-1)".to_owned(),
244        expression: expr.clone(),
245        rule_applied: "Power Rule".to_owned(),
246        latex: Some("n \\cdot x^{n-1}".to_owned()),
247    });
248
249    let n_minus_1 = Expression::add(vec![exp.clone(), Expression::integer(-1)]).simplify();
250    let result = Expression::mul(vec![
251        exp.clone(),
252        Expression::pow(base.clone(), n_minus_1.clone()),
253    ])
254    .simplify();
255
256    steps.push(Step {
257        title: "Simplify".to_owned(),
258        description: format!(
259            "{}*{}^({})",
260            format_expr(exp),
261            format_expr(base),
262            format_expr(&n_minus_1)
263        ),
264        expression: result.clone(),
265        rule_applied: "Simplification".to_owned(),
266        latex: Some(
267            result
268                .to_latex(None)
269                .unwrap_or_else(|_| "result".to_owned()),
270        ),
271    });
272
273    let step_count = steps.len();
274    StepByStepExplanation {
275        initial_expression: expr,
276        final_expression: result,
277        steps,
278        total_steps: step_count,
279        rules_used: vec!["Power Rule".to_owned()],
280    }
281}